1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 //=============================================================================
24 //
25 // AGS Character functions
26 //
27 //=============================================================================
28
29 #include "ags/engine/ac/global_character.h"
30 #include "ags/shared/ac/common.h"
31 #include "ags/shared/ac/view.h"
32 #include "ags/engine/ac/character.h"
33 #include "ags/engine/ac/display.h"
34 #include "ags/engine/ac/draw.h"
35 #include "ags/engine/ac/event.h"
36 #include "ags/shared/ac/game_setup_struct.h"
37 #include "ags/engine/ac/game_state.h"
38 #include "ags/engine/ac/global_overlay.h"
39 #include "ags/engine/ac/global_translation.h"
40 #include "ags/engine/ac/object.h"
41 #include "ags/engine/ac/overlay.h"
42 #include "ags/engine/ac/properties.h"
43 #include "ags/engine/ac/screen_overlay.h"
44 #include "ags/engine/ac/string.h"
45 #include "ags/engine/debugging/debug_log.h"
46 #include "ags/shared/game/room_struct.h"
47 #include "ags/engine/main/game_run.h"
48 #include "ags/engine/script/script.h"
49
50 namespace AGS3 {
51
52 using namespace AGS::Shared;
53
54
55
56
57
58
59
60
61
62 // defined in character unit
63
64
65
66
67
68
StopMoving(int chaa)69 void StopMoving(int chaa) {
70
71 Character_StopMoving(&_GP(game).chars[chaa]);
72 }
73
ReleaseCharacterView(int chat)74 void ReleaseCharacterView(int chat) {
75 if (!is_valid_character(chat))
76 quit("!ReleaseCahracterView: invalid character supplied");
77
78 Character_UnlockView(&_GP(game).chars[chat]);
79 }
80
MoveToWalkableArea(int charid)81 void MoveToWalkableArea(int charid) {
82 if (!is_valid_character(charid))
83 quit("!MoveToWalkableArea: invalid character specified");
84
85 Character_PlaceOnWalkableArea(&_GP(game).chars[charid]);
86 }
87
FaceLocation(int cha,int xx,int yy)88 void FaceLocation(int cha, int xx, int yy) {
89 if (!is_valid_character(cha))
90 quit("!FaceLocation: Invalid character specified");
91
92 Character_FaceLocation(&_GP(game).chars[cha], xx, yy, BLOCKING);
93 }
94
FaceCharacter(int cha,int toface)95 void FaceCharacter(int cha, int toface) {
96 if (!is_valid_character(cha))
97 quit("!FaceCharacter: Invalid character specified");
98 if (!is_valid_character(toface))
99 quit("!FaceCharacter: invalid character specified");
100
101 Character_FaceCharacter(&_GP(game).chars[cha], &_GP(game).chars[toface], BLOCKING);
102 }
103
104
SetCharacterIdle(int who,int iview,int itime)105 void SetCharacterIdle(int who, int iview, int itime) {
106 if (!is_valid_character(who))
107 quit("!SetCharacterIdle: Invalid character specified");
108
109 Character_SetIdleView(&_GP(game).chars[who], iview, itime);
110 }
111
112
113
GetCharacterWidth(int ww)114 int GetCharacterWidth(int ww) {
115 CharacterInfo *char1 = &_GP(game).chars[ww];
116
117 if (_G(charextra)[ww].width < 1) {
118 if ((char1->view < 0) ||
119 (char1->loop >= _G(views)[char1->view].numLoops) ||
120 (char1->frame >= _G(views)[char1->view].loops[char1->loop].numFrames)) {
121 debug_script_warn("GetCharacterWidth: Character %s has invalid frame: view %d, loop %d, frame %d", char1->scrname, char1->view + 1, char1->loop, char1->frame);
122 return data_to_game_coord(4);
123 }
124
125 return _GP(game).SpriteInfos[_G(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Width;
126 } else
127 return _G(charextra)[ww].width;
128 }
129
GetCharacterHeight(int charid)130 int GetCharacterHeight(int charid) {
131 CharacterInfo *char1 = &_GP(game).chars[charid];
132
133 if (_G(charextra)[charid].height < 1) {
134 if ((char1->view < 0) ||
135 (char1->loop >= _G(views)[char1->view].numLoops) ||
136 (char1->frame >= _G(views)[char1->view].loops[char1->loop].numFrames)) {
137 debug_script_warn("GetCharacterHeight: Character %s has invalid frame: view %d, loop %d, frame %d", char1->scrname, char1->view + 1, char1->loop, char1->frame);
138 return data_to_game_coord(2);
139 }
140
141 return _GP(game).SpriteInfos[_G(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Height;
142 } else
143 return _G(charextra)[charid].height;
144 }
145
146
147
SetCharacterBaseline(int obn,int basel)148 void SetCharacterBaseline(int obn, int basel) {
149 if (!is_valid_character(obn)) quit("!SetCharacterBaseline: invalid object number specified");
150
151 Character_SetBaseline(&_GP(game).chars[obn], basel);
152 }
153
154 // pass trans=0 for fully solid, trans=100 for fully transparent
SetCharacterTransparency(int obn,int trans)155 void SetCharacterTransparency(int obn, int trans) {
156 if (!is_valid_character(obn))
157 quit("!SetCharTransparent: invalid character number specified");
158
159 Character_SetTransparency(&_GP(game).chars[obn], trans);
160 }
161
scAnimateCharacter(int chh,int loopn,int sppd,int rept)162 void scAnimateCharacter(int chh, int loopn, int sppd, int rept) {
163 if (!is_valid_character(chh))
164 quit("AnimateCharacter: invalid character");
165
166 animate_character(&_GP(game).chars[chh], loopn, sppd, rept);
167 }
168
AnimateCharacterEx(int chh,int loopn,int sppd,int rept,int direction,int blocking)169 void AnimateCharacterEx(int chh, int loopn, int sppd, int rept, int direction, int blocking) {
170 if ((direction < 0) || (direction > 1))
171 quit("!AnimateCharacterEx: invalid direction");
172 if (!is_valid_character(chh))
173 quit("AnimateCharacter: invalid character");
174
175 if (direction)
176 direction = BACKWARDS;
177 else
178 direction = FORWARDS;
179
180 if (blocking)
181 blocking = BLOCKING;
182 else
183 blocking = IN_BACKGROUND;
184
185 Character_Animate(&_GP(game).chars[chh], loopn, sppd, rept, blocking, direction);
186
187 }
188
189
SetPlayerCharacter(int newchar)190 void SetPlayerCharacter(int newchar) {
191 if (!is_valid_character(newchar))
192 quit("!SetPlayerCharacter: Invalid character specified");
193
194 Character_SetAsPlayer(&_GP(game).chars[newchar]);
195 }
196
FollowCharacterEx(int who,int tofollow,int distaway,int eagerness)197 void FollowCharacterEx(int who, int tofollow, int distaway, int eagerness) {
198 if (!is_valid_character(who))
199 quit("!FollowCharacter: Invalid character specified");
200 CharacterInfo *chtofollow = nullptr;
201 if (tofollow != -1) {
202 if (!is_valid_character(tofollow))
203 quit("!FollowCharacterEx: invalid character to follow");
204 else
205 chtofollow = &_GP(game).chars[tofollow];
206 }
207
208 Character_FollowCharacter(&_GP(game).chars[who], chtofollow, distaway, eagerness);
209 }
210
FollowCharacter(int who,int tofollow)211 void FollowCharacter(int who, int tofollow) {
212 FollowCharacterEx(who, tofollow, 10, 97);
213 }
214
SetCharacterIgnoreLight(int who,int yesorno)215 void SetCharacterIgnoreLight(int who, int yesorno) {
216 if (!is_valid_character(who))
217 quit("!SetCharacterIgnoreLight: Invalid character specified");
218
219 Character_SetIgnoreLighting(&_GP(game).chars[who], yesorno);
220 }
221
222
223
224
MoveCharacter(int cc,int xx,int yy)225 void MoveCharacter(int cc, int xx, int yy) {
226 walk_character(cc, xx, yy, 0, true);
227 }
MoveCharacterDirect(int cc,int xx,int yy)228 void MoveCharacterDirect(int cc, int xx, int yy) {
229 walk_character(cc, xx, yy, 1, true);
230 }
MoveCharacterStraight(int cc,int xx,int yy)231 void MoveCharacterStraight(int cc, int xx, int yy) {
232 if (!is_valid_character(cc))
233 quit("!MoveCharacterStraight: invalid character specified");
234
235 Character_WalkStraight(&_GP(game).chars[cc], xx, yy, IN_BACKGROUND);
236 }
237
238 // Append to character path
MoveCharacterPath(int chac,int tox,int toy)239 void MoveCharacterPath(int chac, int tox, int toy) {
240 if (!is_valid_character(chac))
241 quit("!MoveCharacterPath: invalid character specified");
242
243 Character_AddWaypoint(&_GP(game).chars[chac], tox, toy);
244 }
245
246
GetPlayerCharacter()247 int GetPlayerCharacter() {
248 return _GP(game).playercharacter;
249 }
250
SetCharacterSpeedEx(int chaa,int xspeed,int yspeed)251 void SetCharacterSpeedEx(int chaa, int xspeed, int yspeed) {
252 if (!is_valid_character(chaa))
253 quit("!SetCharacterSpeedEx: invalid character");
254
255 Character_SetSpeed(&_GP(game).chars[chaa], xspeed, yspeed);
256
257 }
258
SetCharacterSpeed(int chaa,int nspeed)259 void SetCharacterSpeed(int chaa, int nspeed) {
260 SetCharacterSpeedEx(chaa, nspeed, nspeed);
261 }
262
SetTalkingColor(int chaa,int ncol)263 void SetTalkingColor(int chaa, int ncol) {
264 if (!is_valid_character(chaa)) quit("!SetTalkingColor: invalid character");
265
266 Character_SetSpeechColor(&_GP(game).chars[chaa], ncol);
267 }
268
SetCharacterSpeechView(int chaa,int vii)269 void SetCharacterSpeechView(int chaa, int vii) {
270 if (!is_valid_character(chaa))
271 quit("!SetCharacterSpeechView: invalid character specified");
272
273 Character_SetSpeechView(&_GP(game).chars[chaa], vii);
274 }
275
SetCharacterBlinkView(int chaa,int vii,int intrv)276 void SetCharacterBlinkView(int chaa, int vii, int intrv) {
277 if (!is_valid_character(chaa))
278 quit("!SetCharacterBlinkView: invalid character specified");
279
280 Character_SetBlinkView(&_GP(game).chars[chaa], vii);
281 Character_SetBlinkInterval(&_GP(game).chars[chaa], intrv);
282 }
283
SetCharacterView(int chaa,int vii)284 void SetCharacterView(int chaa, int vii) {
285 if (!is_valid_character(chaa))
286 quit("!SetCharacterView: invalid character specified");
287
288 Character_LockView(&_GP(game).chars[chaa], vii);
289 }
290
SetCharacterFrame(int chaa,int view,int loop,int frame)291 void SetCharacterFrame(int chaa, int view, int loop, int frame) {
292
293 Character_LockViewFrame(&_GP(game).chars[chaa], view, loop, frame);
294 }
295
296 // similar to SetCharView, but aligns the frame to make it line up
SetCharacterViewEx(int chaa,int vii,int loop,int align)297 void SetCharacterViewEx(int chaa, int vii, int loop, int align) {
298
299 Character_LockViewAligned(&_GP(game).chars[chaa], vii, loop, align);
300 }
301
SetCharacterViewOffset(int chaa,int vii,int xoffs,int yoffs)302 void SetCharacterViewOffset(int chaa, int vii, int xoffs, int yoffs) {
303
304 Character_LockViewOffset(&_GP(game).chars[chaa], vii, xoffs, yoffs);
305 }
306
307
ChangeCharacterView(int chaa,int vii)308 void ChangeCharacterView(int chaa, int vii) {
309 if (!is_valid_character(chaa))
310 quit("!ChangeCharacterView: invalid character specified");
311
312 Character_ChangeView(&_GP(game).chars[chaa], vii);
313 }
314
SetCharacterClickable(int cha,int clik)315 void SetCharacterClickable(int cha, int clik) {
316 if (!is_valid_character(cha))
317 quit("!SetCharacterClickable: Invalid character specified");
318 // make the character clicklabe (reset "No interaction" bit)
319 _GP(game).chars[cha].flags &= ~CHF_NOINTERACT;
320 // if they don't want it clickable, set the relevant bit
321 if (clik == 0)
322 _GP(game).chars[cha].flags |= CHF_NOINTERACT;
323 }
324
SetCharacterIgnoreWalkbehinds(int cha,int clik)325 void SetCharacterIgnoreWalkbehinds(int cha, int clik) {
326 if (!is_valid_character(cha))
327 quit("!SetCharacterIgnoreWalkbehinds: Invalid character specified");
328
329 Character_SetIgnoreWalkbehinds(&_GP(game).chars[cha], clik);
330 }
331
332
MoveCharacterToObject(int chaa,int obbj)333 void MoveCharacterToObject(int chaa, int obbj) {
334 // invalid object, do nothing
335 // this allows MoveCharacterToObject(EGO, GetObjectAt(...));
336 if (!is_valid_object(obbj))
337 return;
338
339 walk_character(chaa, _G(objs)[obbj].x + 5, _G(objs)[obbj].y + 6, 0, true);
340
341 GameLoopUntilNotMoving(&_GP(game).chars[chaa].walking);
342 }
343
MoveCharacterToHotspot(int chaa,int hotsp)344 void MoveCharacterToHotspot(int chaa, int hotsp) {
345 if ((hotsp < 0) || (hotsp >= MAX_ROOM_HOTSPOTS))
346 quit("!MovecharacterToHotspot: invalid hotspot");
347 if (_GP(thisroom).Hotspots[hotsp].WalkTo.X < 1) return;
348 walk_character(chaa, _GP(thisroom).Hotspots[hotsp].WalkTo.X, _GP(thisroom).Hotspots[hotsp].WalkTo.Y, 0, true);
349
350 GameLoopUntilNotMoving(&_GP(game).chars[chaa].walking);
351 }
352
MoveCharacterBlocking(int chaa,int xx,int yy,int direct)353 int MoveCharacterBlocking(int chaa, int xx, int yy, int direct) {
354 if (!is_valid_character(chaa))
355 quit("!MoveCharacterBlocking: invalid character");
356
357 // check if they try to move the player when Hide Player Char is
358 // ticked -- otherwise this will hang the game
359 if (_GP(game).chars[chaa].on != 1) {
360 debug_script_warn("MoveCharacterBlocking: character is turned off (is Hide Player Character selected?) and cannot be moved");
361 return 0;
362 }
363
364 if (direct)
365 MoveCharacterDirect(chaa, xx, yy);
366 else
367 MoveCharacter(chaa, xx, yy);
368
369 GameLoopUntilNotMoving(&_GP(game).chars[chaa].walking);
370
371 return -1; // replicates legacy engine effect
372 }
373
GetCharacterSpeechAnimationDelay(CharacterInfo * cha)374 int GetCharacterSpeechAnimationDelay(CharacterInfo *cha) {
375 if ((_G(loaded_game_file_version) < kGameVersion_312) && (_GP(game).options[OPT_SPEECHTYPE] != 0)) {
376 // legacy versions of AGS assigned a fixed delay to Sierra-style speech only
377 return 5;
378 }
379 if (_GP(game).options[OPT_GLOBALTALKANIMSPD] != 0)
380 return _GP(play).talkanim_speed;
381 else
382 return cha->speech_anim_speed;
383 }
384
RunCharacterInteraction(int cc,int mood)385 void RunCharacterInteraction(int cc, int mood) {
386 if (!is_valid_character(cc))
387 quit("!RunCharacterInteraction: invalid character");
388
389 int passon = -1, cdata = -1;
390 if (mood == MODE_LOOK) passon = 0;
391 else if (mood == MODE_HAND) passon = 1;
392 else if (mood == MODE_TALK) passon = 2;
393 else if (mood == MODE_USE) {
394 passon = 3;
395 cdata = _G(playerchar)->activeinv;
396 _GP(play).usedinv = cdata;
397 } else if (mood == MODE_PICKUP) passon = 5;
398 else if (mood == MODE_CUSTOM1) passon = 6;
399 else if (mood == MODE_CUSTOM2) passon = 7;
400
401 _G(evblockbasename) = "character%d";
402 _G(evblocknum) = cc;
403 if (_G(loaded_game_file_version) > kGameVersion_272) {
404 if (passon >= 0)
405 run_interaction_script(_GP(game).charScripts[cc].get(), passon, 4, (passon == 3));
406 run_interaction_script(_GP(game).charScripts[cc].get(), 4); // any click on char
407 } else {
408 if (passon >= 0)
409 run_interaction_event(_GP(game).intrChar[cc].get(), passon, 4, (passon == 3));
410 run_interaction_event(_GP(game).intrChar[cc].get(), 4); // any click on char
411 }
412 }
413
AreCharObjColliding(int charid,int objid)414 int AreCharObjColliding(int charid, int objid) {
415 if (!is_valid_character(charid))
416 quit("!AreCharObjColliding: invalid character");
417 if (!is_valid_object(objid))
418 quit("!AreCharObjColliding: invalid object number");
419
420 return Character_IsCollidingWithObject(&_GP(game).chars[charid], &_G(scrObj)[objid]);
421 }
422
AreCharactersColliding(int cchar1,int cchar2)423 int AreCharactersColliding(int cchar1, int cchar2) {
424 if (!is_valid_character(cchar1))
425 quit("!AreCharactersColliding: invalid char1");
426 if (!is_valid_character(cchar2))
427 quit("!AreCharactersColliding: invalid char2");
428
429 return Character_IsCollidingWithChar(&_GP(game).chars[cchar1], &_GP(game).chars[cchar2]);
430 }
431
GetCharacterProperty(int cha,const char * property)432 int GetCharacterProperty(int cha, const char *property) {
433 if (!is_valid_character(cha))
434 quit("!GetCharacterProperty: invalid character");
435 return get_int_property(_GP(game).charProps[cha], _GP(play).charProps[cha], property);
436 }
437
SetCharacterProperty(int who,int flag,int yesorno)438 void SetCharacterProperty(int who, int flag, int yesorno) {
439 if (!is_valid_character(who))
440 quit("!SetCharacterProperty: Invalid character specified");
441
442 Character_SetOption(&_GP(game).chars[who], flag, yesorno);
443 }
444
GetCharacterPropertyText(int item,const char * property,char * bufer)445 void GetCharacterPropertyText(int item, const char *property, char *bufer) {
446 get_text_property(_GP(game).charProps[item], _GP(play).charProps[item], property, bufer);
447 }
448
GetCharIDAtScreen(int xx,int yy)449 int GetCharIDAtScreen(int xx, int yy) {
450 VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
451 if (vpt.second < 0)
452 return -1;
453 return is_pos_on_character(vpt.first.X, vpt.first.Y);
454 }
455
SetActiveInventory(int iit)456 void SetActiveInventory(int iit) {
457
458 ScriptInvItem *tosend = nullptr;
459 if ((iit > 0) && (iit < _GP(game).numinvitems))
460 tosend = &_G(scrInv)[iit];
461 else if (iit != -1)
462 quitprintf("!SetActiveInventory: invalid inventory number %d", iit);
463
464 Character_SetActiveInventory(_G(playerchar), tosend);
465 }
466
update_invorder()467 void update_invorder() {
468 for (int cc = 0; cc < _GP(game).numcharacters; cc++) {
469 _G(charextra)[cc].invorder_count = 0;
470 int ff, howmany;
471 // Iterate through all inv items, adding them once (or multiple
472 // times if requested) to the list.
473 for (ff = 0; ff < _GP(game).numinvitems; ff++) {
474 howmany = _GP(game).chars[cc].inv[ff];
475 if ((_GP(game).options[OPT_DUPLICATEINV] == 0) && (howmany > 1))
476 howmany = 1;
477
478 for (int ts = 0; ts < howmany; ts++) {
479 if (_G(charextra)[cc].invorder_count >= MAX_INVORDER)
480 quit("!Too many inventory items to display: 500 max");
481
482 _G(charextra)[cc].invorder[_G(charextra)[cc].invorder_count] = ff;
483 _G(charextra)[cc].invorder_count++;
484 }
485 }
486 }
487 // backwards compatibility
488 _GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
489 GUI::MarkInventoryForUpdate(_GP(game).playercharacter, true);
490 }
491
add_inventory(int inum)492 void add_inventory(int inum) {
493 if ((inum < 0) || (inum >= MAX_INV))
494 quit("!AddInventory: invalid inventory number");
495
496 Character_AddInventory(_G(playerchar), &_G(scrInv)[inum], SCR_NO_VALUE);
497
498 _GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
499 }
500
lose_inventory(int inum)501 void lose_inventory(int inum) {
502 if ((inum < 0) || (inum >= MAX_INV))
503 quit("!LoseInventory: invalid inventory number");
504
505 Character_LoseInventory(_G(playerchar), &_G(scrInv)[inum]);
506
507 _GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
508 }
509
AddInventoryToCharacter(int charid,int inum)510 void AddInventoryToCharacter(int charid, int inum) {
511 if (!is_valid_character(charid))
512 quit("!AddInventoryToCharacter: invalid character specified");
513 if ((inum < 1) || (inum >= _GP(game).numinvitems))
514 quit("!AddInventory: invalid inv item specified");
515
516 Character_AddInventory(&_GP(game).chars[charid], &_G(scrInv)[inum], SCR_NO_VALUE);
517 }
518
LoseInventoryFromCharacter(int charid,int inum)519 void LoseInventoryFromCharacter(int charid, int inum) {
520 if (!is_valid_character(charid))
521 quit("!LoseInventoryFromCharacter: invalid character specified");
522 if ((inum < 1) || (inum >= _GP(game).numinvitems))
523 quit("!AddInventory: invalid inv item specified");
524
525 Character_LoseInventory(&_GP(game).chars[charid], &_G(scrInv)[inum]);
526 }
527
DisplayThought(int chid,const char * text)528 void DisplayThought(int chid, const char *text) {
529 if ((chid < 0) || (chid >= _GP(game).numcharacters))
530 quit("!DisplayThought: invalid character specified");
531
532 _DisplayThoughtCore(chid, text);
533 }
534
__sc_displayspeech(int chid,const char * text)535 void __sc_displayspeech(int chid, const char *text) {
536 if ((chid < 0) || (chid >= _GP(game).numcharacters))
537 quit("!DisplaySpeech: invalid character specified");
538
539 _DisplaySpeechCore(chid, text);
540 }
541
542 // **** THIS IS UNDOCUMENTED BECAUSE IT DOESN'T WORK PROPERLY
543 // **** AT 640x400 AND DOESN'T USE THE RIGHT SPEECH STYLE
DisplaySpeechAt(int xx,int yy,int wii,int aschar,const char * spch)544 void DisplaySpeechAt(int xx, int yy, int wii, int aschar, const char *spch) {
545 data_to_game_coords(&xx, &yy);
546 wii = data_to_game_coord(wii);
547 _displayspeech(get_translation(spch), aschar, xx, yy, wii, 0);
548 }
549
DisplaySpeechBackground(int charid,const char * speel)550 int DisplaySpeechBackground(int charid, const char *speel) {
551 // remove any previous background speech for this character
552 int cc;
553 for (cc = 0; cc < _G(numscreenover); cc++) {
554 if (_G(screenover)[cc].bgSpeechForChar == charid) {
555 remove_screen_overlay_index(cc);
556 cc--;
557 }
558 }
559
560 int ovrl = CreateTextOverlay(OVR_AUTOPLACE, charid, _GP(play).GetUIViewport().GetWidth() / 2, FONT_SPEECH,
561 -_GP(game).chars[charid].talkcolor, get_translation(speel), DISPLAYTEXT_NORMALOVERLAY);
562
563 int scid = find_overlay_of_type(ovrl);
564 _G(screenover)[scid].bgSpeechForChar = charid;
565 _G(screenover)[scid].timeout = GetTextDisplayTime(speel, 1);
566 return ovrl;
567 }
568
569 } // namespace AGS3
570