1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 #include "ac/button.h"
16 #include "ac/common.h"
17 #include "ac/gui.h"
18 #include "ac/view.h"
19 #include "ac/gamesetupstruct.h"
20 #include "ac/global_translation.h"
21 #include "ac/string.h"
22 #include "ac/viewframe.h"
23 #include "debug/debug_log.h"
24 #include "gui/animatingguibutton.h"
25 #include "gui/guimain.h"
26 
27 extern GameSetupStruct game;
28 extern ViewStruct*views;
29 extern int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
30 
31 // *** BUTTON FUNCTIONS
32 
33 AnimatingGUIButton animbuts[MAX_ANIMATING_BUTTONS];
34 int numAnimButs;
35 
Button_Animate(GUIButton * butt,int view,int loop,int speed,int repeat)36 void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat) {
37     int guin = butt->guin;
38     int objn = butt->objn;
39 
40     if ((view < 1) || (view > game.numviews))
41         quit("!AnimateButton: invalid view specified");
42     view--;
43 
44     if ((loop < 0) || (loop >= views[view].numLoops))
45         quit("!AnimateButton: invalid loop specified for view");
46 
47     // if it's already animating, stop it
48     FindAndRemoveButtonAnimation(guin, objn);
49 
50     if (numAnimButs >= MAX_ANIMATING_BUTTONS)
51         quit("!AnimateButton: too many animating GUI buttons at once");
52 
53     int buttonId = guis[guin].CtrlRefs[objn] & 0x000ffff;
54 
55     guibuts[buttonId].pushedpic = 0;
56     guibuts[buttonId].overpic = 0;
57 
58     animbuts[numAnimButs].ongui = guin;
59     animbuts[numAnimButs].onguibut = objn;
60     animbuts[numAnimButs].buttonid = buttonId;
61     animbuts[numAnimButs].view = view;
62     animbuts[numAnimButs].loop = loop;
63     animbuts[numAnimButs].speed = speed;
64     animbuts[numAnimButs].repeat = repeat;
65     animbuts[numAnimButs].frame = -1;
66     animbuts[numAnimButs].wait = 0;
67     numAnimButs++;
68     // launch into the first frame
69     if (UpdateAnimatingButton(numAnimButs - 1))
70         quit("!AnimateButton: no frames to animate");
71 }
72 
Button_GetText_New(GUIButton * butt)73 const char* Button_GetText_New(GUIButton *butt) {
74     return CreateNewScriptString(butt->text);
75 }
76 
Button_GetText(GUIButton * butt,char * buffer)77 void Button_GetText(GUIButton *butt, char *buffer) {
78     strcpy(buffer, butt->text);
79 }
80 
Button_SetText(GUIButton * butt,const char * newtx)81 void Button_SetText(GUIButton *butt, const char *newtx) {
82     newtx = get_translation(newtx);
83     if (strlen(newtx) > 49) quit("!SetButtonText: text too long, button has 50 chars max");
84 
85     if (strcmp(butt->text, newtx)) {
86         guis_need_update = 1;
87         strcpy(butt->text,newtx);
88     }
89 }
90 
Button_SetFont(GUIButton * butt,int newFont)91 void Button_SetFont(GUIButton *butt, int newFont) {
92     if ((newFont < 0) || (newFont >= game.numfonts))
93         quit("!Button.Font: invalid font number.");
94 
95     if (butt->font != newFont) {
96         butt->font = newFont;
97         guis_need_update = 1;
98     }
99 }
100 
Button_GetFont(GUIButton * butt)101 int Button_GetFont(GUIButton *butt) {
102     return butt->font;
103 }
104 
Button_GetClipImage(GUIButton * butt)105 int Button_GetClipImage(GUIButton *butt) {
106     if (butt->flags & GUIF_CLIP)
107         return 1;
108     return 0;
109 }
110 
Button_SetClipImage(GUIButton * butt,int newval)111 void Button_SetClipImage(GUIButton *butt, int newval) {
112     butt->flags &= ~GUIF_CLIP;
113     if (newval)
114         butt->flags |= GUIF_CLIP;
115 
116     guis_need_update = 1;
117 }
118 
Button_GetGraphic(GUIButton * butt)119 int Button_GetGraphic(GUIButton *butt) {
120     // return currently displayed pic
121     if (butt->usepic < 0)
122         return butt->pic;
123     return butt->usepic;
124 }
125 
Button_GetMouseOverGraphic(GUIButton * butt)126 int Button_GetMouseOverGraphic(GUIButton *butt) {
127     return butt->overpic;
128 }
129 
Button_SetMouseOverGraphic(GUIButton * guil,int slotn)130 void Button_SetMouseOverGraphic(GUIButton *guil, int slotn) {
131     debug_script_log("GUI %d Button %d mouseover set to slot %d", guil->guin, guil->objn, slotn);
132 
133     if ((guil->isover != 0) && (guil->ispushed == 0))
134         guil->usepic = slotn;
135     guil->overpic = slotn;
136 
137     guis_need_update = 1;
138     FindAndRemoveButtonAnimation(guil->guin, guil->objn);
139 }
140 
Button_GetNormalGraphic(GUIButton * butt)141 int Button_GetNormalGraphic(GUIButton *butt) {
142     return butt->pic;
143 }
144 
Button_SetNormalGraphic(GUIButton * guil,int slotn)145 void Button_SetNormalGraphic(GUIButton *guil, int slotn) {
146     debug_script_log("GUI %d Button %d normal set to slot %d", guil->guin, guil->objn, slotn);
147     // normal pic - update if mouse is not over, or if there's no overpic
148     if (((guil->isover == 0) || (guil->overpic < 1)) && (guil->ispushed == 0))
149         guil->usepic = slotn;
150     guil->pic = slotn;
151     // update the clickable area to the same size as the graphic
152     guil->wid = spritewidth[slotn];
153     guil->hit = spriteheight[slotn];
154 
155     guis_need_update = 1;
156     FindAndRemoveButtonAnimation(guil->guin, guil->objn);
157 }
158 
Button_GetPushedGraphic(GUIButton * butt)159 int Button_GetPushedGraphic(GUIButton *butt) {
160     return butt->pushedpic;
161 }
162 
Button_SetPushedGraphic(GUIButton * guil,int slotn)163 void Button_SetPushedGraphic(GUIButton *guil, int slotn) {
164     debug_script_log("GUI %d Button %d pushed set to slot %d", guil->guin, guil->objn, slotn);
165 
166     if (guil->ispushed)
167         guil->usepic = slotn;
168     guil->pushedpic = slotn;
169 
170     guis_need_update = 1;
171     FindAndRemoveButtonAnimation(guil->guin, guil->objn);
172 }
173 
Button_GetTextColor(GUIButton * butt)174 int Button_GetTextColor(GUIButton *butt) {
175     return butt->textcol;
176 }
177 
Button_SetTextColor(GUIButton * butt,int newcol)178 void Button_SetTextColor(GUIButton *butt, int newcol) {
179     if (butt->textcol != newcol) {
180         butt->textcol = newcol;
181         guis_need_update = 1;
182     }
183 }
184 
185 extern AnimatingGUIButton animbuts[MAX_ANIMATING_BUTTONS];
186 extern int numAnimButs;
187 
188 // ** start animating buttons code
189 
190 // returns 1 if animation finished
UpdateAnimatingButton(int bu)191 int UpdateAnimatingButton(int bu) {
192     if (animbuts[bu].wait > 0) {
193         animbuts[bu].wait--;
194         return 0;
195     }
196     ViewStruct *tview = &views[animbuts[bu].view];
197 
198     animbuts[bu].frame++;
199 
200     if (animbuts[bu].frame >= tview->loops[animbuts[bu].loop].numFrames)
201     {
202         if (tview->loops[animbuts[bu].loop].RunNextLoop()) {
203             // go to next loop
204             animbuts[bu].loop++;
205             animbuts[bu].frame = 0;
206         }
207         else if (animbuts[bu].repeat) {
208             animbuts[bu].frame = 0;
209             // multi-loop anim, go back
210             while ((animbuts[bu].loop > 0) &&
211                 (tview->loops[animbuts[bu].loop - 1].RunNextLoop()))
212                 animbuts[bu].loop--;
213         }
214         else
215             return 1;
216     }
217 
218     CheckViewFrame(animbuts[bu].view, animbuts[bu].loop, animbuts[bu].frame);
219 
220     // update the button's image
221     guibuts[animbuts[bu].buttonid].pic = tview->loops[animbuts[bu].loop].frames[animbuts[bu].frame].pic;
222     guibuts[animbuts[bu].buttonid].usepic = guibuts[animbuts[bu].buttonid].pic;
223     guibuts[animbuts[bu].buttonid].pushedpic = 0;
224     guibuts[animbuts[bu].buttonid].overpic = 0;
225     guis_need_update = 1;
226 
227     animbuts[bu].wait = animbuts[bu].speed + tview->loops[animbuts[bu].loop].frames[animbuts[bu].frame].speed;
228     return 0;
229 }
230 
StopButtonAnimation(int idxn)231 void StopButtonAnimation(int idxn) {
232     numAnimButs--;
233     for (int aa = idxn; aa < numAnimButs; aa++) {
234         animbuts[aa] = animbuts[aa + 1];
235     }
236 }
237 
238 // Returns the index of the AnimatingGUIButton object corresponding to the
239 // given button ID; returns -1 if no such animation exists
FindAnimatedButton(int guin,int objn)240 int FindAnimatedButton(int guin, int objn)
241 {
242     for (int i = 0; i < numAnimButs; ++i)
243     {
244         if (animbuts[i].ongui == guin && animbuts[i].onguibut == objn)
245             return i;
246     }
247     return -1;
248 }
249 
FindAndRemoveButtonAnimation(int guin,int objn)250 void FindAndRemoveButtonAnimation(int guin, int objn)
251 {
252     int idx = FindAnimatedButton(guin, objn);
253     if (idx >= 0)
254         StopButtonAnimation(idx);
255 }
256 // ** end animating buttons code
257 
Button_Click(GUIButton * butt,int mbut)258 void Button_Click(GUIButton *butt, int mbut)
259 {
260     process_interface_click(butt->guin, butt->objn, mbut);
261 }
262 
Button_IsAnimating(GUIButton * butt)263 bool Button_IsAnimating(GUIButton *butt)
264 {
265     return FindAnimatedButton(butt->guin, butt->objn) >= 0;
266 }
267 
268 // NOTE: in correspondance to similar functions for Character & Object,
269 // GetView returns (view index + 1), while GetLoop and GetFrame return
270 // zero-based index and 0 in case of no animation.
Button_GetAnimView(GUIButton * butt)271 int Button_GetAnimView(GUIButton *butt)
272 {
273     int idx = FindAnimatedButton(butt->guin, butt->objn);
274     return idx >= 0 ? animbuts[idx].view + 1 : 0;
275 }
276 
Button_GetAnimLoop(GUIButton * butt)277 int Button_GetAnimLoop(GUIButton *butt)
278 {
279     int idx = FindAnimatedButton(butt->guin, butt->objn);
280     return idx >= 0 ? animbuts[idx].loop : 0;
281 }
282 
Button_GetAnimFrame(GUIButton * butt)283 int Button_GetAnimFrame(GUIButton *butt)
284 {
285     int idx = FindAnimatedButton(butt->guin, butt->objn);
286     return idx >= 0 ? animbuts[idx].frame : 0;
287 }
288 
289 //=============================================================================
290 //
291 // Script API Functions
292 //
293 //=============================================================================
294 
295 #include "debug/out.h"
296 #include "script/script_api.h"
297 #include "script/script_runtime.h"
298 #include "ac/dynobj/scriptstring.h"
299 
300 extern ScriptString myScriptStringImpl;
301 
302 // void | GUIButton *butt, int view, int loop, int speed, int repeat
Sc_Button_Animate(void * self,const RuntimeScriptValue * params,int32_t param_count)303 RuntimeScriptValue Sc_Button_Animate(void *self, const RuntimeScriptValue *params, int32_t param_count)
304 {
305     API_OBJCALL_VOID_PINT4(GUIButton, Button_Animate);
306 }
307 
308 // const char* | GUIButton *butt
Sc_Button_GetText_New(void * self,const RuntimeScriptValue * params,int32_t param_count)309 RuntimeScriptValue Sc_Button_GetText_New(void *self, const RuntimeScriptValue *params, int32_t param_count)
310 {
311     API_OBJCALL_OBJ(GUIButton, const char, myScriptStringImpl, Button_GetText_New);
312 }
313 
314 // void | GUIButton *butt, char *buffer
Sc_Button_GetText(void * self,const RuntimeScriptValue * params,int32_t param_count)315 RuntimeScriptValue Sc_Button_GetText(void *self, const RuntimeScriptValue *params, int32_t param_count)
316 {
317     API_OBJCALL_VOID_POBJ(GUIButton, Button_GetText, char);
318 }
319 
320 // void | GUIButton *butt, const char *newtx
Sc_Button_SetText(void * self,const RuntimeScriptValue * params,int32_t param_count)321 RuntimeScriptValue Sc_Button_SetText(void *self, const RuntimeScriptValue *params, int32_t param_count)
322 {
323     API_OBJCALL_VOID_POBJ(GUIButton, Button_SetText, const char);
324 }
325 
326 // void | GUIButton *butt, int newFont
Sc_Button_SetFont(void * self,const RuntimeScriptValue * params,int32_t param_count)327 RuntimeScriptValue Sc_Button_SetFont(void *self, const RuntimeScriptValue *params, int32_t param_count)
328 {
329     API_OBJCALL_VOID_PINT(GUIButton, Button_SetFont);
330 }
331 
332 // int | GUIButton *butt
Sc_Button_GetFont(void * self,const RuntimeScriptValue * params,int32_t param_count)333 RuntimeScriptValue Sc_Button_GetFont(void *self, const RuntimeScriptValue *params, int32_t param_count)
334 {
335     API_OBJCALL_INT(GUIButton, Button_GetFont);
336 }
337 
338 // int | GUIButton *butt
Sc_Button_GetClipImage(void * self,const RuntimeScriptValue * params,int32_t param_count)339 RuntimeScriptValue Sc_Button_GetClipImage(void *self, const RuntimeScriptValue *params, int32_t param_count)
340 {
341     API_OBJCALL_INT(GUIButton, Button_GetClipImage);
342 }
343 
344 // void | GUIButton *butt, int newval
Sc_Button_SetClipImage(void * self,const RuntimeScriptValue * params,int32_t param_count)345 RuntimeScriptValue Sc_Button_SetClipImage(void *self, const RuntimeScriptValue *params, int32_t param_count)
346 {
347     API_OBJCALL_VOID_PINT(GUIButton, Button_SetClipImage);
348 }
349 
350 // int | GUIButton *butt
Sc_Button_GetGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)351 RuntimeScriptValue Sc_Button_GetGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
352 {
353     API_OBJCALL_INT(GUIButton, Button_GetGraphic);
354 }
355 
356 // int | GUIButton *butt
Sc_Button_GetMouseOverGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)357 RuntimeScriptValue Sc_Button_GetMouseOverGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
358 {
359     API_OBJCALL_INT(GUIButton, Button_GetMouseOverGraphic);
360 }
361 
362 // void | GUIButton *guil, int slotn
Sc_Button_SetMouseOverGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)363 RuntimeScriptValue Sc_Button_SetMouseOverGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
364 {
365     API_OBJCALL_VOID_PINT(GUIButton, Button_SetMouseOverGraphic);
366 }
367 
368 // int | GUIButton *butt
Sc_Button_GetNormalGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)369 RuntimeScriptValue Sc_Button_GetNormalGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
370 {
371     API_OBJCALL_INT(GUIButton, Button_GetNormalGraphic);
372 }
373 
374 // void | GUIButton *guil, int slotn
Sc_Button_SetNormalGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)375 RuntimeScriptValue Sc_Button_SetNormalGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
376 {
377     API_OBJCALL_VOID_PINT(GUIButton, Button_SetNormalGraphic);
378 }
379 
380 // int | GUIButton *butt
Sc_Button_GetPushedGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)381 RuntimeScriptValue Sc_Button_GetPushedGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
382 {
383     API_OBJCALL_INT(GUIButton, Button_GetPushedGraphic);
384 }
385 
386 // void | GUIButton *guil, int slotn
Sc_Button_SetPushedGraphic(void * self,const RuntimeScriptValue * params,int32_t param_count)387 RuntimeScriptValue Sc_Button_SetPushedGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count)
388 {
389     API_OBJCALL_VOID_PINT(GUIButton, Button_SetPushedGraphic);
390 }
391 
392 // int | GUIButton *butt
Sc_Button_GetTextColor(void * self,const RuntimeScriptValue * params,int32_t param_count)393 RuntimeScriptValue Sc_Button_GetTextColor(void *self, const RuntimeScriptValue *params, int32_t param_count)
394 {
395     API_OBJCALL_INT(GUIButton, Button_GetTextColor);
396 }
397 
398 // void | GUIButton *butt, int newcol
Sc_Button_SetTextColor(void * self,const RuntimeScriptValue * params,int32_t param_count)399 RuntimeScriptValue Sc_Button_SetTextColor(void *self, const RuntimeScriptValue *params, int32_t param_count)
400 {
401     API_OBJCALL_VOID_PINT(GUIButton, Button_SetTextColor);
402 }
403 
Sc_Button_Click(void * self,const RuntimeScriptValue * params,int32_t param_count)404 RuntimeScriptValue Sc_Button_Click(void *self, const RuntimeScriptValue *params, int32_t param_count)
405 {
406     API_OBJCALL_VOID_PINT(GUIButton, Button_Click);
407 }
408 
Sc_Button_GetAnimating(void * self,const RuntimeScriptValue * params,int32_t param_count)409 RuntimeScriptValue Sc_Button_GetAnimating(void *self, const RuntimeScriptValue *params, int32_t param_count)
410 {
411     API_OBJCALL_BOOL(GUIButton, Button_IsAnimating);
412 }
413 
Sc_Button_GetFrame(void * self,const RuntimeScriptValue * params,int32_t param_count)414 RuntimeScriptValue Sc_Button_GetFrame(void *self, const RuntimeScriptValue *params, int32_t param_count)
415 {
416     API_OBJCALL_INT(GUIButton, Button_GetAnimFrame);
417 }
418 
Sc_Button_GetLoop(void * self,const RuntimeScriptValue * params,int32_t param_count)419 RuntimeScriptValue Sc_Button_GetLoop(void *self, const RuntimeScriptValue *params, int32_t param_count)
420 {
421     API_OBJCALL_INT(GUIButton, Button_GetAnimLoop);
422 }
423 
Sc_Button_GetView(void * self,const RuntimeScriptValue * params,int32_t param_count)424 RuntimeScriptValue Sc_Button_GetView(void *self, const RuntimeScriptValue *params, int32_t param_count)
425 {
426     API_OBJCALL_INT(GUIButton, Button_GetAnimView);
427 }
428 
RegisterButtonAPI()429 void RegisterButtonAPI()
430 {
431     ccAddExternalObjectFunction("Button::Animate^4",            Sc_Button_Animate);
432     ccAddExternalObjectFunction("Button::Click^1",              Sc_Button_Click);
433     ccAddExternalObjectFunction("Button::GetText^1",            Sc_Button_GetText);
434     ccAddExternalObjectFunction("Button::SetText^1",            Sc_Button_SetText);
435     ccAddExternalObjectFunction("Button::get_Animating",        Sc_Button_GetAnimating);
436     ccAddExternalObjectFunction("Button::get_ClipImage",        Sc_Button_GetClipImage);
437     ccAddExternalObjectFunction("Button::set_ClipImage",        Sc_Button_SetClipImage);
438     ccAddExternalObjectFunction("Button::get_Font",             Sc_Button_GetFont);
439     ccAddExternalObjectFunction("Button::set_Font",             Sc_Button_SetFont);
440     ccAddExternalObjectFunction("Button::get_Frame",            Sc_Button_GetFrame);
441     ccAddExternalObjectFunction("Button::get_Graphic",          Sc_Button_GetGraphic);
442     ccAddExternalObjectFunction("Button::get_Loop",             Sc_Button_GetLoop);
443     ccAddExternalObjectFunction("Button::get_MouseOverGraphic", Sc_Button_GetMouseOverGraphic);
444     ccAddExternalObjectFunction("Button::set_MouseOverGraphic", Sc_Button_SetMouseOverGraphic);
445     ccAddExternalObjectFunction("Button::get_NormalGraphic",    Sc_Button_GetNormalGraphic);
446     ccAddExternalObjectFunction("Button::set_NormalGraphic",    Sc_Button_SetNormalGraphic);
447     ccAddExternalObjectFunction("Button::get_PushedGraphic",    Sc_Button_GetPushedGraphic);
448     ccAddExternalObjectFunction("Button::set_PushedGraphic",    Sc_Button_SetPushedGraphic);
449     ccAddExternalObjectFunction("Button::get_Text",             Sc_Button_GetText_New);
450     ccAddExternalObjectFunction("Button::set_Text",             Sc_Button_SetText);
451     ccAddExternalObjectFunction("Button::get_TextColor",        Sc_Button_GetTextColor);
452     ccAddExternalObjectFunction("Button::set_TextColor",        Sc_Button_SetTextColor);
453     ccAddExternalObjectFunction("Button::get_View",             Sc_Button_GetView);
454 
455     /* ----------------------- Registering unsafe exports for plugins -----------------------*/
456 
457     ccAddExternalFunctionForPlugin("Button::Animate^4",            (void*)Button_Animate);
458     ccAddExternalFunctionForPlugin("Button::GetText^1",            (void*)Button_GetText);
459     ccAddExternalFunctionForPlugin("Button::SetText^1",            (void*)Button_SetText);
460     ccAddExternalFunctionForPlugin("Button::get_ClipImage",        (void*)Button_GetClipImage);
461     ccAddExternalFunctionForPlugin("Button::set_ClipImage",        (void*)Button_SetClipImage);
462     ccAddExternalFunctionForPlugin("Button::get_Font",             (void*)Button_GetFont);
463     ccAddExternalFunctionForPlugin("Button::set_Font",             (void*)Button_SetFont);
464     ccAddExternalFunctionForPlugin("Button::get_Graphic",          (void*)Button_GetGraphic);
465     ccAddExternalFunctionForPlugin("Button::get_MouseOverGraphic", (void*)Button_GetMouseOverGraphic);
466     ccAddExternalFunctionForPlugin("Button::set_MouseOverGraphic", (void*)Button_SetMouseOverGraphic);
467     ccAddExternalFunctionForPlugin("Button::get_NormalGraphic",    (void*)Button_GetNormalGraphic);
468     ccAddExternalFunctionForPlugin("Button::set_NormalGraphic",    (void*)Button_SetNormalGraphic);
469     ccAddExternalFunctionForPlugin("Button::get_PushedGraphic",    (void*)Button_GetPushedGraphic);
470     ccAddExternalFunctionForPlugin("Button::set_PushedGraphic",    (void*)Button_SetPushedGraphic);
471     ccAddExternalFunctionForPlugin("Button::get_Text",             (void*)Button_GetText_New);
472     ccAddExternalFunctionForPlugin("Button::set_Text",             (void*)Button_SetText);
473     ccAddExternalFunctionForPlugin("Button::get_TextColor",        (void*)Button_GetTextColor);
474     ccAddExternalFunctionForPlugin("Button::set_TextColor",        (void*)Button_SetTextColor);
475 }
476