1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 
4 #include "CobEngine.h"
5 #include "CobFile.h"
6 #include "CobInstance.h"
7 #include "CobThread.h"
8 #include "UnitScriptLog.h"
9 
10 #ifndef _CONSOLE
11 
12 #include "Game/GameHelper.h"
13 #include "Game/GlobalUnsynced.h"
14 #include "Map/Ground.h"
15 #include "Sim/Misc/GroundBlockingObjectMap.h"
16 #include "Sim/Misc/LosHandler.h"
17 #include "Sim/Misc/RadarHandler.h"
18 #include "Sim/Misc/TeamHandler.h"
19 #include "Sim/Projectiles/ExplosionGenerator.h"
20 #include "Sim/Projectiles/PieceProjectile.h"
21 #include "Sim/Projectiles/ProjectileHandler.h"
22 #include "Sim/Projectiles/Unsynced/BubbleProjectile.h"
23 #include "Sim/Projectiles/Unsynced/HeatCloudProjectile.h"
24 #include "Sim/Projectiles/Unsynced/MuzzleFlame.h"
25 #include "Sim/Projectiles/Unsynced/SmokeProjectile.h"
26 #include "Sim/Projectiles/Unsynced/WakeProjectile.h"
27 #include "Sim/Projectiles/Unsynced/WreckProjectile.h"
28 #include "Sim/Units/CommandAI/CommandAI.h"
29 #include "Sim/Units/CommandAI/Command.h"
30 #include "Sim/Units/UnitDef.h"
31 #include "Sim/Units/Unit.h"
32 #include "Sim/Units/UnitHandler.h"
33 #include "Sim/Units/UnitTypes/TransportUnit.h"
34 #include "Sim/Weapons/BeamLaser.h"
35 #include "Sim/Weapons/PlasmaRepulser.h"
36 #include "Sim/Weapons/WeaponDefHandler.h"
37 #include "Sim/Weapons/Weapon.h"
38 #include "System/Util.h"
39 #include "System/myMath.h"
40 #include "System/Sound/ISoundChannels.h"
41 #include "System/Sync/SyncTracer.h"
42 
43 #endif // _CONSOLE
44 
45 
46 /******************************************************************************/
47 /******************************************************************************/
48 
49 
HasFunction(int id) const50 inline bool CCobInstance::HasFunction(int id) const
51 {
52 	return script.scriptIndex[id] >= 0;
53 }
54 
55 
CCobInstance(CCobFile & _script,CUnit * _unit)56 CCobInstance::CCobInstance(CCobFile& _script, CUnit* _unit)
57 	: CUnitScript(_unit, pieces)
58 	, script(_script)
59 {
60 	staticVars.reserve(script.numStaticVars);
61 	for (int i = 0; i < script.numStaticVars; ++i) {
62 		staticVars.push_back(0);
63 	}
64 
65 	MapScriptToModelPieces(unit->localModel);
66 
67 	hasSetSFXOccupy  = HasFunction(COBFN_SetSFXOccupy);
68 	hasRockUnit      = HasFunction(COBFN_RockUnit);
69 	hasStartBuilding = HasFunction(COBFN_StartBuilding);
70 }
71 
72 
~CCobInstance()73 CCobInstance::~CCobInstance()
74 {
75 	//this may be dangerous, is it really desired?
76 	//Destroy();
77 
78 	do {
79 		for (int animType = ATurn; animType <= AMove; animType++) {
80 			for (std::list<AnimInfo *>::iterator i = anims[animType].begin(); i != anims[animType].end(); ++i) {
81 				// All threads blocking on animations can be killed safely from here since the scheduler does not
82 				// know about them
83 				std::list<IAnimListener *>& listeners = (*i)->listeners;
84 				while (!listeners.empty()) {
85 					IAnimListener* al = listeners.front();
86 					listeners.pop_front();
87 					delete al;
88 				}
89 				// the anims are deleted in ~CUnitScript
90 			}
91 		}
92 		// callbacks may add new threads, and therefore listeners
93 	} while (HaveListeners());
94 
95 	// Can't delete the thread here because that would confuse the scheduler to no end
96 	// Instead, mark it as dead. It is the function calling Tick that is responsible for delete.
97 	// Also unregister all callbacks
98 	for (std::list<CCobThread *>::iterator i = threads.begin(); i != threads.end(); ++i) {
99 		(*i)->state = CCobThread::Dead;
100 		(*i)->SetCallback(NULL, NULL, NULL);
101 	}
102 }
103 
104 
MapScriptToModelPieces(LocalModel * lmodel)105 void CCobInstance::MapScriptToModelPieces(LocalModel* lmodel)
106 {
107 	std::vector<std::string>& pieceNames = script.pieceNames; // already in lowercase!
108 	std::vector<LocalModelPiece*>& lmodelPieces = lmodel->pieces;
109 
110 	pieces.clear();
111 	pieces.reserve(pieceNames.size());
112 
113 	// clear the default assumed 1:1 mapping
114 	for (size_t lmodelPieceNum = 0; lmodelPieceNum < lmodelPieces.size(); lmodelPieceNum++) {
115 		lmodelPieces[lmodelPieceNum]->SetScriptPieceIndex(-1);
116 	}
117 	for (size_t scriptPieceNum = 0; scriptPieceNum < pieceNames.size(); scriptPieceNum++) {
118 		unsigned int lmodelPieceNum;
119 
120 		// Map this piecename to an index in the script's pieceinfo
121 		for (lmodelPieceNum = 0; lmodelPieceNum < lmodelPieces.size(); lmodelPieceNum++) {
122 			if (lmodelPieces[lmodelPieceNum]->original->name.compare(pieceNames[scriptPieceNum]) == 0) {
123 				break;
124 			}
125 		}
126 
127 		// Not found? Try lowercase
128 		if (lmodelPieceNum == lmodelPieces.size()) {
129 			for (lmodelPieceNum = 0; lmodelPieceNum < lmodelPieces.size(); lmodelPieceNum++) {
130 				if (StringToLower(lmodelPieces[lmodelPieceNum]->original->name).compare(pieceNames[scriptPieceNum]) == 0) {
131 					break;
132 				}
133 			}
134 		}
135 
136 		// Did we find it?
137 		if (lmodelPieceNum < lmodelPieces.size()) {
138 			lmodelPieces[lmodelPieceNum]->SetScriptPieceIndex(scriptPieceNum);
139 			pieces.push_back(lmodelPieces[lmodelPieceNum]);
140 		} else {
141 			pieces.push_back(NULL);
142 
143 			const char* fmtString = "[%s] could not find piece named \"%s\" (referenced by COB script \"%s\")";
144 			const char* pieceName = pieceNames[scriptPieceNum].c_str();
145 			const char* scriptName = script.name.c_str();
146 
147 			LOG_L(L_WARNING, fmtString, __FUNCTION__, pieceName, scriptName);
148 		}
149 	}
150 }
151 
152 
GetFunctionId(const std::string & fname) const153 int CCobInstance::GetFunctionId(const std::string& fname) const
154 {
155 	return script.GetFunctionId(fname);
156 }
157 
158 
HasBlockShot(int weaponNum) const159 bool CCobInstance::HasBlockShot(int weaponNum) const
160 {
161 	return HasFunction(COBFN_BlockShot + COBFN_Weapon_Funcs * weaponNum);
162 }
163 
164 
HasTargetWeight(int weaponNum) const165 bool CCobInstance::HasTargetWeight(int weaponNum) const
166 {
167 	return HasFunction(COBFN_TargetWeight + COBFN_Weapon_Funcs * weaponNum);
168 }
169 
170 
171 /******************************************************************************/
172 /******************************************************************************/
173 
174 
Create()175 void CCobInstance::Create()
176 {
177 	// Calculate the max() of the available weapon reloadtimes
178 	int maxReloadTime = 0;
179 
180 	for (vector<CWeapon*>::iterator i = unit->weapons.begin(); i != unit->weapons.end(); ++i) {
181 		maxReloadTime = std::max(maxReloadTime, (*i)->reloadTime);
182 
183 		#if 0
184 		if (dynamic_cast<CBeamLaser*>(*i))
185 			maxReloadTime = 150; // ???
186 		#endif
187 	}
188 
189 	// convert ticks to milliseconds
190 	maxReloadTime *= GAME_SPEED;
191 
192 	#if 0
193 	// TA does some special handling depending on weapon count, Spring != TA
194 	if (unit->weapons.size() > 1) {
195 		maxReloadTime = std::max(maxReloadTime, 3000);
196 	}
197 	#endif
198 
199 	Call(COBFN_Create);
200 	Call(COBFN_SetMaxReloadTime, maxReloadTime);
201 }
202 
203 
204 // Called when a unit's Killed script finishes executing
CUnitKilledCB(int retCode,void * p1,void * p2)205 static void CUnitKilledCB(int retCode, void* p1, void* p2)
206 {
207 	CUnit* self = static_cast<CUnit*>(p1);
208 	self->deathScriptFinished = true;
209 	self->delayedWreckLevel = retCode;
210 }
211 
212 
Killed()213 void CCobInstance::Killed()
214 {
215 	vector<int> args;
216 	args.reserve(2);
217 	args.push_back((int) (unit->recentDamage / unit->maxHealth * 100));
218 	args.push_back(0);
219 	Call(COBFN_Killed, args, &CUnitKilledCB, unit, NULL);
220 	unit->delayedWreckLevel = args[1];
221 }
222 
223 
WindChanged(float heading,float speed)224 void CCobInstance::WindChanged(float heading, float speed)
225 {
226 	Call(COBFN_SetSpeed, (int)(speed * 3000.0f));
227 	Call(COBFN_SetDirection, short(heading * RAD2TAANG));
228 }
229 
230 
ExtractionRateChanged(float speed)231 void CCobInstance::ExtractionRateChanged(float speed)
232 {
233 	Call(COBFN_SetSpeed, (int)(speed * 500.0f));
234 	if (unit->activated) {
235 		Call(COBFN_Go);
236 	}
237 }
238 
239 
RockUnit(const float3 & rockDir)240 void CCobInstance::RockUnit(const float3& rockDir)
241 {
242 	vector<int> args;
243 	args.reserve(2);
244 	args.push_back((int)(500 * rockDir.z));
245 	args.push_back((int)(500 * rockDir.x));
246 	Call(COBFN_RockUnit, args);
247 }
248 
249 
250 // ugly hack to get return value of HitByWeaponId script
251 static float weaponHitMod; ///< fraction of weapondamage to use when hit by weapon
hitByWeaponIdCallback(int retCode,void * p1,void * p2)252 static void hitByWeaponIdCallback(int retCode, void* p1, void* p2) { weaponHitMod = retCode * 0.01f; }
253 
254 
HitByWeapon(const float3 & hitDir,int weaponDefId,float & inout_damage)255 void CCobInstance::HitByWeapon(const float3& hitDir, int weaponDefId, float& inout_damage)
256 {
257 	vector<int> args;
258 	args.reserve(4);
259 
260 	args.push_back((int)(500 * hitDir.z));
261 	args.push_back((int)(500 * hitDir.x));
262 
263 	if (HasFunction(COBFN_HitByWeaponId)) {
264 		const WeaponDef* wd = weaponDefHandler->GetWeaponDefByID(weaponDefId);
265 		args.push_back(wd ? wd->tdfId : -1);
266 		args.push_back((int)(100 * inout_damage));
267 
268 		weaponHitMod = 1.0f;
269 		Call(COBFN_HitByWeaponId, args, hitByWeaponIdCallback, NULL, NULL);
270 		inout_damage *= weaponHitMod; // weaponHitMod gets set in callback function
271 	}
272 	else {
273 		Call(COBFN_HitByWeapon, args);
274 	}
275 }
276 
277 
SetSFXOccupy(int curTerrainType)278 void CCobInstance::SetSFXOccupy(int curTerrainType)
279 {
280 	Call(COBFN_SetSFXOccupy, curTerrainType);
281 }
282 
283 
QueryLandingPads(std::vector<int> & out_pieces)284 void CCobInstance::QueryLandingPads(std::vector<int>& out_pieces)
285 {
286 	int maxPadCount = 16; // default max pad count
287 
288 	if (HasFunction(COBFN_QueryLandingPadCount)) {
289 		vector<int> args;
290 		args.push_back(maxPadCount);
291 		Call(COBFN_QueryLandingPadCount, args);
292 		maxPadCount = args[0];
293 	}
294 
295 	for (int i = 0; i < maxPadCount; i++) {
296 		out_pieces.push_back(-1);
297 	}
298 
299 	Call(COBFN_QueryLandingPad, out_pieces);
300 }
301 
302 
BeginTransport(const CUnit * unit)303 void CCobInstance::BeginTransport(const CUnit* unit)
304 {
305 	// yes, COB is silly, while it handles integers fine it uses model height to identify units
306 	Call(COBFN_BeginTransport, (int)(unit->model->height*65536));
307 }
308 
309 
QueryTransport(const CUnit * unit)310 int CCobInstance::QueryTransport(const CUnit* unit)
311 {
312 	vector<int> args;
313 	args.reserve(2);
314 	args.push_back(0);
315 	args.push_back((int)(unit->model->height*65536));
316 	Call(COBFN_QueryTransport, args);
317 	return args[0];
318 }
319 
320 
TransportPickup(const CUnit * unit)321 void CCobInstance::TransportPickup(const CUnit* unit)
322 {
323 	// funny, now it uses unitIDs instead of model height
324 	Call(COBFN_TransportPickup, unit->id);
325 }
326 
327 
TransportDrop(const CUnit * unit,const float3 & pos)328 void CCobInstance::TransportDrop(const CUnit* unit, const float3& pos)
329 {
330 	vector<int> args;
331 	args.reserve(2);
332 	args.push_back(unit->id);
333 	args.push_back(PACKXZ(pos.x, pos.z));
334 	Call(COBFN_TransportDrop, args);
335 }
336 
337 
StartBuilding(float heading,float pitch)338 void CCobInstance::StartBuilding(float heading, float pitch)
339 {
340 	vector<int> args;
341 	args.reserve(2);
342 	args.push_back(short(heading * RAD2TAANG));
343 	args.push_back(short(pitch * RAD2TAANG));
344 	Call(COBFN_StartBuilding, args);
345 }
346 
347 
QueryNanoPiece()348 int CCobInstance::QueryNanoPiece()
349 {
350 	vector<int> args(1, 0);
351 	Call(COBFN_QueryNanoPiece, args);
352 	return args[0];
353 }
354 
355 
QueryBuildInfo()356 int CCobInstance::QueryBuildInfo()
357 {
358 	vector<int> args(1, 0);
359 	Call(COBFN_QueryBuildInfo, args);
360 	return args[0];
361 }
362 
363 
QueryWeapon(int weaponNum)364 int CCobInstance::QueryWeapon(int weaponNum)
365 {
366 	vector<int> args(1, 0);
367 	Call(COBFN_QueryPrimary + COBFN_Weapon_Funcs * weaponNum, args);
368 	return args[0];
369 }
370 
371 
372 // Called when unit's AimWeapon script finished executing
ScriptCallback(int retCode,void * p1,void * p2)373 static void ScriptCallback(int retCode, void* p1, void* p2)
374 {
375 	static_cast<CWeapon*>(p1)->angleGood = (retCode == 1);
376 }
377 
AimWeapon(int weaponNum,float heading,float pitch)378 void CCobInstance::AimWeapon(int weaponNum, float heading, float pitch)
379 {
380 	vector<int> args;
381 	args.reserve(2);
382 	args.push_back(short(heading * RAD2TAANG));
383 	args.push_back(short(pitch * RAD2TAANG));
384 	Call(COBFN_AimPrimary + COBFN_Weapon_Funcs * weaponNum, args, ScriptCallback, unit->weapons[weaponNum], NULL);
385 }
386 
387 
388 // Called when unit's AimWeapon script finished executing (for shield weapon)
ShieldScriptCallback(int retCode,void * p1,void * p2)389 static void ShieldScriptCallback(int retCode, void* p1, void* p2)
390 {
391 	static_cast<CPlasmaRepulser*>(p1)->SetEnabled(!!retCode);
392 }
393 
AimShieldWeapon(CPlasmaRepulser * weapon)394 void CCobInstance::AimShieldWeapon(CPlasmaRepulser* weapon)
395 {
396 	vector<int> args;
397 	args.reserve(2);
398 	args.push_back(0); // compat with AimWeapon (same script is called)
399 	args.push_back(0);
400 	Call(COBFN_AimPrimary + COBFN_Weapon_Funcs * weapon->weaponNum, args, ShieldScriptCallback, weapon, 0);
401 }
402 
403 
AimFromWeapon(int weaponNum)404 int CCobInstance::AimFromWeapon(int weaponNum)
405 {
406 	vector<int> args(1, 0);
407 	Call(COBFN_AimFromPrimary + COBFN_Weapon_Funcs * weaponNum, args);
408 	return args[0];
409 }
410 
411 
Shot(int weaponNum)412 void CCobInstance::Shot(int weaponNum)
413 {
414 	Call(COBFN_Shot + COBFN_Weapon_Funcs * weaponNum, 0); // why the 0 argument?
415 }
416 
417 
BlockShot(int weaponNum,const CUnit * targetUnit,bool userTarget)418 bool CCobInstance::BlockShot(int weaponNum, const CUnit* targetUnit, bool userTarget)
419 {
420 	const int unitID = targetUnit ? targetUnit->id : 0;
421 
422 	vector<int> args;
423 	args.reserve(3);
424 
425 	args.push_back(unitID);
426 	args.push_back(0); // arg[1], for the return value
427 	                   // the default is to not block the shot
428 	args.push_back(userTarget);
429 
430 	Call(COBFN_BlockShot + COBFN_Weapon_Funcs * weaponNum, args);
431 
432 	return !!args[1];
433 }
434 
435 
TargetWeight(int weaponNum,const CUnit * targetUnit)436 float CCobInstance::TargetWeight(int weaponNum, const CUnit* targetUnit)
437 {
438 	const int unitID = targetUnit ? targetUnit->id : 0;
439 
440 	vector<int> args;
441 	args.reserve(2);
442 
443 	args.push_back(unitID);
444 	args.push_back(COBSCALE); // arg[1], for the return value
445 	                          // the default is 1.0
446 
447 	Call(COBFN_TargetWeight + COBFN_Weapon_Funcs * weaponNum, args);
448 
449 	return (float)args[1] / (float)COBSCALE;
450 }
451 
452 
Destroy()453 void CCobInstance::Destroy() { Call(COBFN_Destroy); }
StartMoving(bool reversing)454 void CCobInstance::StartMoving(bool reversing) { Call(COBFN_StartMoving, reversing); }
StopMoving()455 void CCobInstance::StopMoving() { Call(COBFN_StopMoving); }
StartUnload()456 void CCobInstance::StartUnload() { Call(COBFN_StartUnload); }
EndTransport()457 void CCobInstance::EndTransport() { Call(COBFN_EndTransport); }
StartBuilding()458 void CCobInstance::StartBuilding() { Call(COBFN_StartBuilding); }
StopBuilding()459 void CCobInstance::StopBuilding() { Call(COBFN_StopBuilding); }
Falling()460 void CCobInstance::Falling() { Call(COBFN_Falling); }
Landed()461 void CCobInstance::Landed() { Call(COBFN_Landed); }
Activate()462 void CCobInstance::Activate() { Call(COBFN_Activate); }
Deactivate()463 void CCobInstance::Deactivate() { Call(COBFN_Deactivate); }
MoveRate(int curRate)464 void CCobInstance::MoveRate(int curRate) { Call(COBFN_MoveRate0 + curRate); }
FireWeapon(int weaponNum)465 void CCobInstance::FireWeapon(int weaponNum) { Call(COBFN_FirePrimary + COBFN_Weapon_Funcs * weaponNum); }
EndBurst(int weaponNum)466 void CCobInstance::EndBurst(int weaponNum) { Call(COBFN_EndBurst + COBFN_Weapon_Funcs * weaponNum); }
467 
468 
469 /******************************************************************************/
470 
471 
472 /**
473  * @brief Calls a cob script function
474  * @param functionId int cob script function id
475  * @param args vector<int> function arguments
476  * @param cb CBCobThreadFinish Callback function
477  * @param p1 void* callback argument #1
478  * @param p2 void* callback argument #2
479  * @return 0 if the call terminated. If the caller provides a callback and the thread does not terminate,
480  *  it will continue to run. Otherwise it will be killed. Returns 1 in this case.
481  */
RealCall(int functionId,vector<int> & args,CBCobThreadFinish cb,void * p1,void * p2)482 int CCobInstance::RealCall(int functionId, vector<int>& args, CBCobThreadFinish cb, void* p1, void* p2)
483 {
484 	if (functionId < 0 || size_t(functionId) >= script.scriptNames.size()) {
485 		if (cb) {
486 			// in case the function doesnt exist the callback should still be called
487 			(*cb)(0, p1, p2);
488 		}
489 		return -1;
490 	}
491 
492 	CCobThread* thread = new CCobThread(script, this);
493 	thread->Start(functionId, args, false);
494 
495 	LOG_L(L_DEBUG, "Calling %s:%s", script.name.c_str(), script.scriptNames[functionId].c_str());
496 
497 	const bool res = thread->Tick();
498 
499 	// Make sure this is run even if the call terminates instantly
500 	if (cb)
501 		thread->SetCallback(cb, p1, p2);
502 
503 	if (!res) {
504 		// thread died already after one tick
505 		// NOTE:
506 		//   the StartMoving callin now takes an argument which means
507 		//   there will be a mismatch between the number of arguments
508 		//   passed in (1) and the number returned (0) as of 95.0 -->
509 		//   prevent error-spam
510 		unsigned int i = 0, argc = thread->CheckStack(args.size(), functionId != script.scriptIndex[COBFN_StartMoving]);
511 
512 		// Retrieve parameter values from stack
513 		for (; i < argc; ++i)
514 			args[i] = thread->GetStackVal(i);
515 
516 		// Set erroneous parameters to 0
517 		for (; i < args.size(); ++i)
518 			args[i] = 0;
519 
520 		delete thread;
521 		return 0;
522 	}
523 
524 	// thread has already added itself to the correct
525 	// scheduler (global for sleep, or local for anim)
526 	return 1;
527 }
528 
529 
530 /******************************************************************************/
531 
532 
Call(const std::string & fname)533 int CCobInstance::Call(const std::string& fname)
534 {
535 	vector<int> x;
536 	return Call(fname, x, NULL, NULL, NULL);
537 }
538 
Call(const std::string & fname,std::vector<int> & args)539 int CCobInstance::Call(const std::string& fname, std::vector<int>& args)
540 {
541 	return Call(fname, args, NULL, NULL, NULL);
542 }
543 
Call(const std::string & fname,int p1)544 int CCobInstance::Call(const std::string& fname, int p1)
545 {
546 	vector<int> x;
547 	x.reserve(1);
548 	x.push_back(p1);
549 	return Call(fname, x, NULL, NULL, NULL);
550 }
551 
Call(const std::string & fname,std::vector<int> & args,CBCobThreadFinish cb,void * p1,void * p2)552 int CCobInstance::Call(const std::string& fname, std::vector<int>& args, CBCobThreadFinish cb, void* p1, void* p2)
553 {
554 	//TODO: Check that new behaviour of actually calling cb when the function is not defined is right?
555 	//      (the callback has always been called [when the function is not defined]
556 	//       in the id-based Call()s, but never in the string based calls.)
557 	return RealCall(GetFunctionId(fname), args, cb, p1, p2);
558 }
559 
560 
Call(int id)561 int CCobInstance::Call(int id)
562 {
563 	vector<int> x;
564 	return Call(id, x, NULL, NULL, NULL);
565 }
566 
Call(int id,int p1)567 int CCobInstance::Call(int id, int p1)
568 {
569 	vector<int> x;
570 	x.reserve(1);
571 	x.push_back(p1);
572 	return Call(id, x, NULL, NULL, NULL);
573 }
574 
Call(int id,std::vector<int> & args)575 int CCobInstance::Call(int id, std::vector<int>& args)
576 {
577 	return Call(id, args, NULL, NULL, NULL);
578 }
579 
Call(int id,std::vector<int> & args,CBCobThreadFinish cb,void * p1,void * p2)580 int CCobInstance::Call(int id, std::vector<int>& args, CBCobThreadFinish cb, void* p1, void* p2)
581 {
582 	return RealCall(script.scriptIndex[id], args, cb, p1, p2);
583 }
584 
585 
RawCall(int fn)586 void CCobInstance::RawCall(int fn)
587 {
588 	vector<int> x;
589 	RealCall(fn, x, NULL, NULL, NULL);
590 }
591 
RawCall(int fn,std::vector<int> & args)592 int CCobInstance::RawCall(int fn, std::vector<int> &args)
593 {
594 	return RealCall(fn, args, NULL, NULL, NULL);
595 }
596 
597 
598 /******************************************************************************/
599 /******************************************************************************/
600 
601 
Signal(int signal)602 void CCobInstance::Signal(int signal)
603 {
604 	for (std::list<CCobThread *>::iterator i = threads.begin(); i != threads.end(); ++i) {
605 		if ((signal & (*i)->signalMask) != 0) {
606 			(*i)->state = CCobThread::Dead;
607 			//LOG_L(L_DEBUG, "Killing a thread %d %d", signal, (*i)->signalMask);
608 		}
609 	}
610 }
611 
612 
PlayUnitSound(int snr,int attr)613 void CCobInstance::PlayUnitSound(int snr, int attr)
614 {
615 	Channels::UnitReply->PlaySample(script.sounds[snr], unit->pos, unit->speed, attr);
616 }
617 
618 
ShowScriptError(const std::string & msg)619 void CCobInstance::ShowScriptError(const std::string& msg)
620 {
621 	GCobEngine.ShowScriptError(msg);
622 }
623