1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "uInput.h"
29 #include "tMemManager.h"
30 #include "rScreen.h"
31 #include "tInitExit.h"
32 #include "tConfiguration.h"
33 #include "rConsole.h"
34 #include "uMenu.h"
35 #include "tSysTime.h"
36 
37 bool su_mouseGrab = false;
38 
39 static uAction* su_allActions[uMAX_ACTIONS];
40 static int     su_allActionsLen = 0;
41 
uAction(uAction * & anchor,const char * name,int priority_,uInputType t)42 uAction::uAction(uAction *&anchor,const char* name,
43                  int priority_,
44                  uInputType t)
45         :tListItem<uAction>(anchor),type(t),priority(priority_),internalName(name){
46     globalID = localID = su_allActionsLen++;
47 
48     tASSERT(localID < uMAX_ACTIONS);
49 
50     su_allActions[localID] = this;
51 
52     tString descname;
53     descname << "input_" << name << "_text";
54     tToLower( descname );
55 
56     const_cast<tOutput&>(description).AddLocale(descname);
57 
58     tString helpname;
59     helpname << "input_" << name << "_help";
60     tToLower( helpname );
61 
62     const_cast<tOutput&>(helpText).AddLocale(helpname);
63 }
64 
uAction(uAction * & anchor,const char * name,const tOutput & desc,const tOutput & help,int priority_,uInputType t)65 uAction::uAction(uAction *&anchor,const char* name,
66                  const tOutput& desc,
67                  const tOutput& help,
68                  int priority_,
69                  uInputType t)
70         :tListItem<uAction>(anchor),type(t),priority(priority_),internalName(name), description(desc), helpText(help){
71     globalID = localID = su_allActionsLen++;
72 
73     tASSERT(localID < uMAX_ACTIONS);
74 
75     su_allActions[localID] = this;
76 }
77 
~uAction()78 uAction::~uAction(){
79     su_allActions[localID] = NULL;
80 }
81 
Find(char const * name)82 uAction * uAction::Find( char const * name )
83 {
84     for(int i=su_allActionsLen-1;i>=0;i--)
85         if(!strcmp(name,su_allActions[i]->internalName))
86             return su_allActions[i];
87 
88     return 0;
89 }
90 
91 // ****************************************
92 // a configuration class for keyboard binds
93 // ****************************************
94 
95 class tConfItem_key:public tConfItemBase{
96 public:
tConfItem_key()97     tConfItem_key():tConfItemBase("KEYBOARD"){}
~tConfItem_key()98     ~tConfItem_key(){}
99 
100     // write the complete keymap
WriteVal(std::ostream & s)101     virtual void WriteVal(std::ostream &s){
102         int first=1;
103         for(int keysym=SDLK_NEWLAST-1;keysym>=0;keysym--){
104             if (keymap[keysym]){
105 
106                 if (!first)
107                     s << "\nKEYBOARD\t";
108                 else
109                     first=0;
110 
111                 s << keysym << '\t';
112                 keymap[keysym]->Write(s);
113             }
114         }
115         if (first)
116             s << "-1";
117     }
118 
119     // read one keybind
ReadVal(std::istream & s)120     virtual void ReadVal(std::istream &s){
121         tString in;
122         int keysym;
123         s >> keysym;
124         if (keysym>=0){
125             tASSERT(keysym < SDLK_NEWLAST);
126             s >> in;
127             if (uBindPlayer::IsKeyWord(in))
128             {
129                 keymap[keysym] = NULL;
130                 keymap[keysym]=uBindPlayer::NewBind(s);
131                 if (!keymap[keysym]->act)
132                 {
133                     keymap[keysym]=NULL;
134                 }
135                 /* if (global_bind::IsKeyWord(in))
136                    keymap[keysym]=new global_bind(s); */
137             }
138         }
139         char c=' ';
140         while(c!='\n' && s.good() && !s.eof()) c=s.get();
141     }
142 };
143 
144 // we need just one
145 static tConfItem_key x;
146 
147 static uAction *s_playerActions;
148 static uAction *s_cameraActions;
149 static uAction *s_globalActions;
150 
uActionPlayer(const char * name,int priority,uInputType t)151 uActionPlayer::uActionPlayer(const char *name,
152                              int priority,
153                              uInputType t)
154     :uAction(s_playerActions,name,priority,t){}
155 
uActionPlayer(const char * name,const tOutput & desc,const tOutput & help,int priority,uInputType t)156 uActionPlayer::uActionPlayer(const char *name,
157                              const tOutput& desc,
158                              const tOutput& help,
159                              int priority,
160                              uInputType t)
161         :uAction(s_playerActions,name,desc,help,priority,t){}
162 
~uActionPlayer()163 uActionPlayer::~uActionPlayer(){}
164 
operator ==(const uActionPlayer & x)165 bool uActionPlayer::operator==(const uActionPlayer &x){
166     return x.globalID == globalID;}
167 
Find(int id)168 uActionPlayer *uActionPlayer::Find(int id){
169     uAction *run = s_playerActions;
170 
171     while (run){
172         if (run->ID() == id)
173             return static_cast<uActionPlayer*>(run);
174         run = run->Next();
175     }
176 
177     return NULL;
178 }
179 
180 
uActionCamera(const char * name,int priority,uInputType t)181 uActionCamera::uActionCamera(const char *name,
182                              int priority,
183                              uInputType t)
184     :uAction(s_cameraActions,name,priority,t){}
185 
~uActionCamera()186 uActionCamera::~uActionCamera(){}
187 
operator ==(const uActionCamera & x)188 bool uActionCamera::operator==(const uActionCamera &x){
189     return x.globalID == globalID;}
190 
191 
192 // global actions
uActionGlobal(const char * name,int priority,uInputType t)193 uActionGlobal::uActionGlobal(const char *name,
194                              int priority,
195                              uInputType t)
196         :uAction(s_globalActions,name,priority,t){}
197 
~uActionGlobal()198 uActionGlobal::~uActionGlobal(){}
199 
operator ==(const uActionGlobal & x)200 bool uActionGlobal::operator==(const uActionGlobal &x){
201     return x.globalID == globalID;}
202 
IsBreakingGlobalBind(int sym)203 bool uActionGlobal::IsBreakingGlobalBind(int sym){
204     if (!keymap[sym])
205         return false;
206     uAction *act=keymap[sym]->act;
207     if (!act)
208         return false;
209 
210     return uActionGlobalFunc::IsBreakingGlobalBind(act);
211 }
212 
213 // ***************************
214 //    the generic keymaps
215 // ***************************
216 
217 tJUST_CONTROLLED_PTR< uBind > keymap[SDLK_NEWLAST];
218 bool                          pressed[SDLK_NEWLAST];
219 
keyboard_init()220 static void keyboard_init(){
221     for(int i=0;i<SDLK_NEWLAST;i++)
222         keymap[i]=NULL;
223     //uBindPlayer::Init();
224     //global_bind::Init();
225 }
226 
keyboard_exit()227 static void keyboard_exit(){
228     for(int i=0;i<SDLK_NEWLAST;i++)
229         keymap[i] = 0;
230     //uBindPlayer::Init();
231     //global_bind::Init();
232 }
233 
234 static tInitExit keyboard_ie(&keyboard_init, &keyboard_exit);
235 
236 // *********************************************
237 // generic keypress/mouse movement binding class
238 // *********************************************
239 
~uBind()240 uBind::~uBind(){}
241 
uBind(uAction * a)242 uBind::uBind(uAction *a ):lastValue_(0), delayedValue_(0), lastSym_(-1), lastTime_(-1), act(a){}
243 
uBind(std::istream & s)244 uBind::uBind(std::istream &s): lastValue_(0), delayedValue_(0), lastSym_(-1), lastTime_(-1), act(NULL)
245 {
246     std::string name;
247     s >> name;
248     act = uAction::Find( name.c_str() );
249 }
250 
Write(std::ostream & s)251 void uBind::Write(std::ostream &s){
252     s << act->internalName << '\t';
253 }
254 
GlobalAct(uAction * act,REAL x)255 bool GlobalAct(uAction *act,REAL x){
256     return uActionGlobalFunc::GlobalAct(act,x);
257 }
258 
Activate(REAL x,bool delayed)259 bool uBind::Activate(REAL x, bool delayed )
260 {
261     delayedValue_ = x;
262 
263     if ( !delayed || !Delayable() )
264     {
265         lastValue_ = x;
266         return this->DoActivate( x );
267     }
268 
269     return true;
270 }
271 
HanldeDelayed()272 void uBind::HanldeDelayed()
273 {
274     if ( lastValue_ != delayedValue_ )
275     {
276         lastValue_ = delayedValue_;
277         this->DoActivate( delayedValue_ );
278     }
279 }
280 
281 REAL su_doubleBindTimeout=-10.0f;
282 
IsDoubleBind(int sym)283 bool uBind::IsDoubleBind( int sym )
284 {
285     double currentTime = tSysTimeFloat();
286 
287     // if a different key was used for this action a short while ago, give alarm.
288     bool ret = ( su_doubleBindTimeout > 0 && sym != lastSym_ && currentTime - lastTime_ < su_doubleBindTimeout );
289 
290     // store last usage
291     lastSym_ = sym;
292     lastTime_ = currentTime;
293 
294     // return result
295     return ret;
296 }
297 
298 // *******************
299 // player config
300 // *******************
301 
302 static int nextid = 0;
303 
PlayerConfig(int i)304 uPlayerPrototype* uPlayerPrototype::PlayerConfig(int i){
305     tASSERT(i>=0 && i<uMAX_PLAYERS);
306     return playerConfig[i];
307 }
308 
309 
uPlayerPrototype()310 uPlayerPrototype::uPlayerPrototype(){
311     static bool inited=false;
312     if (!inited)
313     {
314         for(int i=uMAX_PLAYERS-1; i >=0; i--)
315             playerConfig[i] = NULL;
316 
317         inited = true;
318     }
319 
320     id = nextid++;
321     tASSERT(id < uMAX_PLAYERS);
322     playerConfig[id] = this;
323 
324 
325 }
326 
~uPlayerPrototype()327 uPlayerPrototype::~uPlayerPrototype(){
328     playerConfig[id] = NULL;
329 }
330 
331 uPlayerPrototype* uPlayerPrototype::playerConfig[uMAX_PLAYERS];
332 
Num()333 int uPlayerPrototype::Num(){return nextid;}
334 
335 // *******************
336 // Input configuration
337 // *******************
338 
339 
340 // *****************************************************
341 //  Menuitem for input selection
342 // *****************************************************
343 
keyname(int sym)344 static char const * keyname(int sym){
345 #ifndef DEDICATED
346     if (sym<=SDLK_LAST)
347         return SDL_GetKeyName(static_cast<SDLKey>(sym));
348     else switch (sym){
349         case SDLK_MOUSE_X_PLUS: return "Mouse right";
350         case SDLK_MOUSE_X_MINUS: return "Mouse left";
351         case SDLK_MOUSE_Y_PLUS: return "Mouse up";
352         case SDLK_MOUSE_Y_MINUS: return "Mouse down";
353         case SDLK_MOUSE_Z_PLUS: return "Mouse z up";
354         case SDLK_MOUSE_Z_MINUS: return "Mouse z down";
355         case SDLK_MOUSE_BUTTON_1: return "Mousebutton 1";
356         case SDLK_MOUSE_BUTTON_2: return "Mousebutton 2";
357         case SDLK_MOUSE_BUTTON_3: return "Mousebutton 3";
358         case SDLK_MOUSE_BUTTON_4: return "Mousebutton 4";
359         case SDLK_MOUSE_BUTTON_5: return "Mousebutton 5";
360         case SDLK_MOUSE_BUTTON_6: return "Mousebutton 6";
361         case SDLK_MOUSE_BUTTON_7: return "Mousebutton 7";
362         }
363 #endif
364     return "";
365 }
366 
367 class uMenuItemInput: uMenuItem{
368     uAction      *act;
369     int         ePlayer;
370     bool        active;
371 public:
uMenuItemInput(uMenu * M,uAction * a,int p)372     uMenuItemInput(uMenu *M,uAction *a,int p)
373             :uMenuItem(M,a->helpText),act(a),ePlayer(p),active(0){
374     }
375 
~uMenuItemInput()376     virtual ~uMenuItemInput(){}
377 
Render(REAL x,REAL y,REAL alpha=1,bool selected=0)378     virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0){
379         DisplayText(REAL(x-.02),y,act->description,selected,alpha,1);
380 
381         if (active)
382         {
383             tString s;
384             s << tOutput("$input_press_any_key");
385             DisplayText(REAL(x+.02),y,s,selected,alpha,-1);
386         }
387         else{
388             tString s;
389 
390             bool first=1;
391 
392             for(int keysym=SDLK_NEWLAST-1;keysym>=0;keysym--)
393                 if(keymap[keysym] &&
394                         keymap[keysym]->act==act &&
395                         keymap[keysym]->CheckPlayer(ePlayer)){
396                     if (!first)
397                         s << ", ";
398                     else
399                         first=0;
400 
401                     s << keyname(keysym);
402                 }
403             if(!first)
404                 DisplayText(REAL(x+.02),y,s,selected,alpha,-1);
405             else
406                 DisplayText(REAL(x+.02),y,tOutput("$input_items_unbound"),selected,alpha,-1);
407         }
408     }
409 
Enter()410     virtual void Enter(){
411         active=1;
412     }
413 
414 #define MTHRESH 5
415 #define MREL    2
416 
417 #ifndef DEDICATED
418 
Event(SDL_Event & e)419     virtual bool Event(SDL_Event &e){
420         int sym=-1;
421         switch (e.type){
422         case SDL_MOUSEMOTION:
423             if(active){
424                 REAL xrel=e.motion.xrel;
425                 REAL yrel=-e.motion.yrel;
426 
427                 if (fabs(xrel)>MREL*fabs(yrel)){ // x motion
428                     if (xrel>MTHRESH) // left
429                         sym=SDLK_MOUSE_X_PLUS;
430                     if (xrel<-MTHRESH) // left
431                         sym=SDLK_MOUSE_X_MINUS;
432                 }
433 
434                 if (fabs(yrel)>MREL*fabs(xrel)){ // x motion
435                     if (yrel>MTHRESH) // left
436                         sym=SDLK_MOUSE_Y_PLUS;
437                     if (yrel<-MTHRESH) // left
438                         sym=SDLK_MOUSE_Y_MINUS;
439                 }
440 
441                 if (sym>0)
442                     active=0;
443             }
444 
445             break;
446         case SDL_MOUSEBUTTONDOWN:
447             if(active){
448                 int button=e.button.button;
449                 if (button<=MOUSE_BUTTONS)
450                     sym=SDLK_MOUSE_BUTTON_1+button-1;
451 
452                 active=0;
453             }
454             break;
455 
456         case SDL_KEYDOWN:{
457                 SDL_keysym &c=e.key.keysym;
458                 if(!active){
459                     if (c.sym==SDLK_DELETE || c.sym==SDLK_BACKSPACE)
460                     {
461                         for(int keysym=SDLK_NEWLAST-1;keysym>=0;keysym--)
462                             if(keymap[keysym] &&
463                                     keymap[keysym]->act==act &&
464                                     keymap[keysym]->CheckPlayer(ePlayer)){
465                                 keymap[keysym]=NULL;
466                             }
467                         return true;
468                     }
469                     return false;
470                 }
471 
472                 active=0;
473 
474                 if (c.sym!=SDLK_ESCAPE)
475                     sym=c.sym;
476                 else
477                     return true;
478             }
479             break;
480         default:
481             return(false);
482         }
483 
484         if(sym>=0){
485             if(keymap[sym] &&
486                     keymap[sym]->act==act &&
487                     keymap[sym]->CheckPlayer(ePlayer)){
488                 keymap[sym]=NULL;
489             }
490             else{
491                 keymap[sym]=NULL;
492                 keymap[sym]=uBindPlayer::NewBind(act,ePlayer);
493             }
494             return true;
495         }
496         return false;
497     }
498 #endif
499 
Help()500     virtual tString Help(){
501         tString ret;
502         ret << helpText << "\n";
503         ret << tOutput("$input_item_help");
504         return ret;
505     }
506 };
507 
508 namespace
509 {
510 class Input_Comparator
511 {
512 public:
Compare(const uAction * a,const uAction * b)513     static int Compare( const uAction* a, const uAction* b )
514     {
515         if ( a->priority < b->priority )
516             return 1;
517         else if ( a->priority > b->priority )
518             return -1;
519         return tString::CompareAlphaNumerical( a->internalName, b->internalName );
520     }
521 };
522 }
523 
Input_Compare(const tListItemBase * a,const tListItemBase * b)524 static int Input_Compare( const tListItemBase* a, const tListItemBase* b)
525 
526 {
527     return Input_Comparator::Compare( (const uAction*)a, (const uAction*)b );
528 }
529 
530 
s_InputConfigGeneric(int ePlayer,uAction * & actions,const tOutput & title)531 static void s_InputConfigGeneric(int ePlayer, uAction *&actions,const tOutput &title){
532     uMenuItemInput **input;
533 
534     uMenu input_menu(title);
535 
536     if(actions)
537         actions->tListItemBase::Sort(&Input_Compare);
538 
539     int len = actions->Len();
540 
541     input=tNEW(uMenuItemInput*)[len];
542     int a=0;
543     for(uAction *A=actions;A; A = A->Next()){
544         input[a++]=new uMenuItemInput(&input_menu,
545                                       A,
546                                       ePlayer+1);
547 
548     }
549 
550     input_menu.ReverseItems();
551     input_menu.Enter();
552 
553     for(int b=a-1;b>=0;b--)
554         delete input[b];
555     delete[] input;
556 }
557 
su_InputConfig(int ePlayer)558 void su_InputConfig(int ePlayer){
559 
560     tOutput name;
561     name.SetTemplateParameter(1, ePlayer+1);
562     name.SetTemplateParameter(2, uPlayerPrototype::PlayerConfig(ePlayer)->Name());
563     name << "$input_for_player";
564 
565     s_InputConfigGeneric(ePlayer,s_playerActions,name);
566 }
567 
su_InputConfigCamera(int player)568 void su_InputConfigCamera(int player){
569 
570     tOutput name;
571     name.SetTemplateParameter(1, uPlayerPrototype::PlayerConfig(player)->Name());
572     name << "$camera_controls";
573 
574     s_InputConfigGeneric(player,s_cameraActions,name);
575 }
576 
su_InputConfigGlobal()577 void su_InputConfigGlobal(){
578     s_InputConfigGeneric(-1,s_globalActions,"$input_items_global");
579 }
580 
581 
582 REAL mouse_sensitivity=REAL(.1);
583 REAL key_sensitivity=40;
584 static double lastTime=0;
585 static REAL ts=0;
586 
587 static bool su_delayed = false;
588 
su_HandleDelayedEvents()589 void su_HandleDelayedEvents ()
590 {
591     // nothing to do
592     if ( !su_delayed )
593     {
594         return;
595     }
596 
597     su_delayed = false;
598 
599     for ( int i = SDLK_NEWLAST - 1; i>=0; --i )
600     {
601         if ( keymap[i] )
602         {
603             keymap[i]->HanldeDelayed();
604         }
605     }
606 }
607 
su_HandleEvent(SDL_Event & e,bool delayed)608 bool su_HandleEvent(SDL_Event &e, bool delayed ){
609 #ifndef DEDICATED
610     int sym=-1;
611     REAL pm=0;
612 
613     if ( su_delayed && !delayed )
614     {
615         su_HandleDelayedEvents();
616     }
617 
618     su_delayed = delayed;
619 
620     // there is nearly alllways a mouse motion tEvent:
621     int xrel=e.motion.xrel;
622     int yrel=-e.motion.yrel;
623 
624 
625     switch (e.type){
626     case SDL_MOUSEMOTION:
627         if ( !su_mouseGrab ||
628                 e.motion.x!=sr_screenWidth/2 || e.motion.x!=sr_screenHeight/2)
629         {
630             if (keymap[SDLK_MOUSE_X_PLUS])
631                 keymap[SDLK_MOUSE_X_PLUS]->Activate(xrel*mouse_sensitivity, delayed );
632 
633             if (keymap[SDLK_MOUSE_X_MINUS])
634                 keymap[SDLK_MOUSE_X_MINUS]->Activate(-xrel*mouse_sensitivity, delayed );
635 
636             if (keymap[SDLK_MOUSE_Y_PLUS])
637                 keymap[SDLK_MOUSE_Y_PLUS]->Activate(yrel*mouse_sensitivity, delayed );
638 
639             if (keymap[SDLK_MOUSE_Y_MINUS])
640                 keymap[SDLK_MOUSE_Y_MINUS]->Activate(-yrel*mouse_sensitivity, delayed );
641         }
642 
643 
644         return true; // no fuss: allways pretend to have handled this.
645         break;
646 
647     case SDL_MOUSEBUTTONDOWN:
648     case SDL_MOUSEBUTTONUP:{
649             int button=e.button.button;
650             if (button<=MOUSE_BUTTONS){
651                 sym=SDLK_MOUSE_BUTTON_1+button-1;
652             }
653         }
654         if (e.type==SDL_MOUSEBUTTONDOWN)
655             pm=1;
656         else
657             pm=-1;
658         break;
659 
660     case SDL_KEYDOWN:
661         sym=e.key.keysym.sym;
662         pm=1;
663         break;
664 
665     case SDL_KEYUP:
666         sym=e.key.keysym.sym;
667         pm=-1;
668         break;
669 
670     default:
671         break;
672     }
673     if (sym>=0 && keymap[sym]){
674         REAL realpm=pm;
675         if (keymap[sym]->act->type==uAction::uINPUT_ANALOG)
676             pm*=ts*key_sensitivity;
677         pressed[sym]=(realpm>0);
678         if ( pm > 0 && keymap[sym]->IsDoubleBind( sym ) )
679             return true;
680         return (keymap[sym]->Activate(pm, delayed ));
681 
682     }
683     else
684 #endif
685         return false;
686 }
687 
su_InputSync()688 void su_InputSync(){
689     double time=tSysTimeFloat();
690     ts=REAL(time-lastTime);
691 
692     //static REAL tsSmooth=0;
693     //tsSmooth+=REAL(ts*.1);
694     //tsSmooth/=REAL(1.1);
695     lastTime=time;
696 
697     for(int sym=SDLK_NEWLAST-1;sym>=0;sym--)
698         if (pressed[sym] && keymap[sym] &&
699                 keymap[sym]->act->type==uAction::uINPUT_ANALOG)
700             keymap[sym]->Activate(ts*key_sensitivity, su_delayed );
701 }
702 
su_ClearKeys()703 void su_ClearKeys()
704 {
705     for(int sym=SDLK_NEWLAST-1;sym>=0;sym--)
706     {
707         if (pressed[sym] && keymap[sym] )
708             keymap[sym]->Activate(-1, su_delayed );
709         pressed[sym] = false;
710     }
711 }
712 
713 // *****************
714 // Player binds
715 // *****************
716 
717 static char const * Player_keyword="PLAYER_BIND";
718 
uBindPlayer(uAction * a,int p)719 uBindPlayer::uBindPlayer(uAction *a,int p):uBind(a),ePlayer(p){}
720 
~uBindPlayer()721 uBindPlayer::~uBindPlayer(){}
722 
NewBind(std::istream & s)723 uBindPlayer * uBindPlayer::NewBind(std::istream &s)
724 {
725     // read action
726     std::string actionName;
727     s >> actionName;
728     uAction * act = uAction::Find( actionName.c_str() );
729 
730     // read player ID
731     int player;
732     s >> player;
733 
734     // delegate
735     return NewBind( act, player );
736 }
737 
NewBind(uAction * action,int player)738 uBindPlayer * uBindPlayer::NewBind( uAction * action, int player )
739 {
740     // see if the bind has an alias
741     for ( int i = SDLK_NEWLAST-1; i >= 0; --i )
742     {
743         // compare action
744         uBind * old = keymap[i];
745         if ( old && old->act == action )
746         {
747             uBindPlayer * oldPlayer = dynamic_cast< uBindPlayer * >( old );
748             if ( oldPlayer && oldPlayer->ePlayer == player )
749                 return oldPlayer;
750         }
751     }
752 
753     // no alias found, return new bind
754     return tNEW(uBindPlayer)( action, player );
755 }
756 
IsKeyWord(const char * n)757 bool uBindPlayer::IsKeyWord(const char *n){
758     return !strcmp(n,Player_keyword);
759 }
760 
CheckPlayer(int p)761 bool uBindPlayer::CheckPlayer(int p){
762     return p==ePlayer;
763 }
764 
Write(std::ostream & s)765 void uBindPlayer::Write(std::ostream &s){
766     s << Player_keyword << '\t';
767     uBind::Write(s);
768     s << ePlayer;
769 }
770 
Delayable()771 bool uBindPlayer::Delayable()
772 {
773     return ( ePlayer!=0 );
774 }
775 
DoActivate(REAL x)776 bool uBindPlayer::DoActivate(REAL x){
777     if (ePlayer==0)
778         return  GlobalAct(act,x);
779     else
780         return uPlayerPrototype::PlayerConfig(ePlayer-1)->Act(act,x);
781 }
782 
783 
784 // *****************
785 // Global actions
786 // *****************
787 
788 static uActionGlobalFunc *uActionGlobal_anchor=NULL;
789 
uActionGlobalFunc(uActionGlobal * a,ACTION_FUNC * f,bool rebind)790 uActionGlobalFunc::uActionGlobalFunc(uActionGlobal *a, ACTION_FUNC *f,
791                                      bool rebind )
792         :tListItem<uActionGlobalFunc>(uActionGlobal_anchor), func (f), act(a),
793 rebindable(rebind){}
794 
IsBreakingGlobalBind(uAction * act)795 bool uActionGlobalFunc::IsBreakingGlobalBind(uAction *act){
796     for(uActionGlobalFunc *run = uActionGlobal_anchor; run ; run = run->Next())
797         if (run->act == act && !run->rebindable)
798             return true;
799 
800     return false;
801 }
802 
GlobalAct(uAction * act,REAL x)803 bool uActionGlobalFunc::GlobalAct(uAction *act, REAL x){
804     for(uActionGlobalFunc *run = uActionGlobal_anchor; run ; run = run->Next())
805         if (run->act == act && run->func(x))
806             return true;
807 
808     return false;
809 }
810 
811 static uActionGlobal mess_up("MESS_UP");
812 
813 static uActionGlobal mess_down("MESS_DOWN");
814 
messup_func(REAL x)815 static bool messup_func(REAL x){
816     if (x>0){
817         sr_con.Scroll(-1);
818     }
819     return true;
820 }
821 
messdown_func(REAL x)822 static bool messdown_func(REAL x){
823     if (x>0){
824         sr_con.Scroll(1);
825     }
826     return true;
827 }
828 
829 static uActionGlobalFunc mu(&mess_up,&messup_func);
830 static uActionGlobalFunc md(&mess_down,&messdown_func);
831