1 /*
2  *
3  *  Iter Vehemens ad Necem (IVAN)
4  *  Copyright (C) Timo Kiviluoto
5  *  Released under the GNU General
6  *  Public License
7  *
8  *  See LICENSING which should be included
9  *  along with this file for more details
10  *
11  */
12 
13 #include "actions.h"
14 #include "bitmap.h"
15 #include "char.h"
16 #include "command.h"
17 #include "confdef.h"
18 #include "craft.h"
19 #include "database.h"
20 #include "devcons.h"
21 #include "felist.h"
22 #include "game.h"
23 #include "gear.h"
24 #include "god.h"
25 #include "graphics.h"
26 #include "human.h"
27 #include "iconf.h"
28 #include "lterras.h"
29 #include "materia.h"
30 #include "materias.h"
31 #include "message.h"
32 #include "miscitem.h"
33 #include "nonhuman.h"
34 #include "proto.h"
35 #include "room.h"
36 #include "specialkeys.h"
37 #include "stack.h"
38 #include "team.h"
39 #include "whandler.h"
40 #include "worldmap.h"
41 #include "wsquare.h"
42 #include "wterras.h"
43 
44 #include "dbgmsgproj.h"
45 
46 #ifdef WIZARD
47 #include "proto.h"
48 #endif
49 
50 #include "cmdcraft.cpp"
51 #include "cmdswapweap.cpp"
52 
command(truth (* LinkedFunction)(character *),cchar * Description,char Key1,char Key2,char Key3,truth UsableInWilderness,truth WizardModeFunction)53 command::command(truth (*LinkedFunction)(character*), cchar* Description, char Key1, char Key2, char Key3,
54                  truth UsableInWilderness, truth WizardModeFunction)
55 : LinkedFunction(LinkedFunction), Description(Description), Key1(Key1), Key2(Key2), Key3(Key3), Key4(0),
56   UsableInWilderness(UsableInWilderness), WizardModeFunction(WizardModeFunction)
57 {
58   game::ValidateCommandKeys(Key1,Key2,Key3);
59 }
60 
GetKey() const61 int command::GetKey() const
62 {
63   if(ivanconfig::IsSetupCustomKeys()){
64     if(Key4>0)
65       return Key4;
66   }
67 
68   switch(ivanconfig::GetDirectionKeyMap())
69   {
70    case DIR_NORM: // Normal
71     return Key1;
72    case DIR_ALT: // Alternative
73     return Key2;
74    case DIR_HACK: // Nethack
75     return Key3;
76    default:
77     ABORT("This is not Vim!");
78     return Key1; //?
79   }
80 }
81 
82 command* commandsystem::Command[] =
83 {
84   0,
85 
86   /* Sort according to relaiton and assumed frequency of use */
87 
88   new command(&NOP, "wait a turn", '.', '.', '.', true),
89   new command(&Go, "go / fastwalk", 'g', 'g', 'g', false),
90   new command(&GoDown, "go down / enter area", '>', '>', '>', true),
91   new command(&GoUp, "go up", '<', '<', '<', true),
92   new command(&PickUp, "pick up item", ',', ',', ',', false),
93   new command(&Drop, "drop item", 'd', 'd', 'd', true),
94   new command(&Throw, "throw item", 't', 't', 't', false),
95   new command(&EquipmentScreen, "equipment menu", 'E', 'E', 'E', true),
96   new command(&ShowInventory, "inventory menu", 'i', 'i', 'i', true),
97   new command(&Apply, "apply item", 'a', 'a', 'a', false),
98   new command(&ApplyAgain, "apply last item again", 'A', 'A', 'A', false),
99   new command(&Zap, "zap a wand", 'z', 'z', 'z', false),
100   new command(&Read, "read", 'r', 'r', 'r', false),
101   new command(&Eat, "eat", 'e', 'e', 'e', true),
102   new command(&Drink, "drink liquid", 'D', 'D', 'D', true),
103   new command(&Taste, "taste a bit of liquid", 'T', 'T', 'T', true),
104   new command(&Dip, "dip into liquid", '!', '!', '!', false),
105   new command(&Open, "open", 'o', 'O', 'o', false),
106   new command(&Close, "close", 'c', 'c', 'c', false),
107   new command(&Search, "search", 's', 's', 's', false),
108   new command(&Look, "look around", 'l', 'L', 'L', true),
109   new command(&ShowMap, "show map", 'm', 'm', 'm', false),
110   new command(&WhatToEngrave, "engrave / inscribe", 'G', 'G', 'G', false),
111   new command(&Talk, "chat", 'C', 'C', 'C', false),
112   new command(&Craft, "craft", 'f', 'F', 'f', false),
113   new command(&AssignName, "name team members", 'n', 'n', 'N', false),
114   new command(&IssueCommand, "issue commands to team members", 'I', 'I', 'I', false),
115   new command(&Offer, "offer to gods", 'O', 'f', 'O', false),
116   new command(&Pray, "pray to gods", 'p', 'p', 'p', false),
117   new command(&Sit, "sit down", '_', '_', '_', false),
118   new command(&Rest, "rest and heal", 'h', 'h', 'H', true),
119   new command(&Save, "save and quit", 'S', 'S', 'S', true),
120   new command(&Quit, "quit and abandon", 'Q', 'Q', 'Q', true),
121   new command(&DrawMessageHistory, "show message history", 'M', 'M', 'M', true),
122   new command(&ScrollMessagesDown, "scroll messages down", '+', '+', '+', true),
123   new command(&ScrollMessagesUp, "scroll messages up", '-', '-', '-', true),
124   new command(&ShowConfigScreen, "show options menu", '\\', '\\', '\\', true),
125   new command(&ShowKeyLayout, "show key layout", '?', '?', '?', true),
126   new command(&ShowWeaponSkills, "show weapon skills", '@', '@', '@', true),
127   new command(&WieldInRightArm, "wield in right hand", 'w', 'w', 'w', true),
128   new command(&WieldInLeftArm, "wield in left hand", 'W', 'W', 'W', true),
129   new command(&SwapWeapons, "swap weapons", 'x', 'x', 'x', false),
130   new command(&SwapWeaponsCfg, "swapping menu", 'X', 'X', 'X', false),
131   new command(&ToggleRunning, "toggle running", 'u', 'U', 'U', true),
132   new command(&Kick, "kick", 'k', 'K', 'K', false),
133   new command(&ForceVomit, "vomit", 'V', 'V', 'V', false),
134   new command(&ShowWorldSeed, "display the world seed", '^', '^', '^', true),
135 
136 #ifdef WIZARD
137   new command(&WizardMode, "wizard mode activation (Ctrl+ for console)", '`', '`', '`', true),
138 #else
139   new command(&DevConsCmd, "access console commands", '`', '`', '`', true), //works w/o Ctrl in this case
140 #endif
141 
142 #ifdef WIZARD
143 
144   /* Sort according to key */
145 
146   new command(&AutoPlay, "auto play the game (hold ESC to stop)", '~', '~', '~', true, true), //there is more AI than NPC's one to let it work better
147   new command(&RaiseStats, "raise stats", '1', '1', '1', true, true),
148   new command(&LowerStats, "lower stats", '2', '2', '2', true, true),
149   new command(&SeeWholeMap, "see whole map", '3', '3', '3', true, true),
150   new command(&WalkThroughWalls, "toggle walk through walls mode", '4', '4', '4', true, true),
151   new command(&RaiseGodRelations, "raise your relations to the gods", '5', '5', '5', true, true),
152   new command(&LowerGodRelations, "lower your relations to the gods", '6', '6', '6', true, true),
153   new command(&GainDivineKnowledge, "gain knowledge of all gods", '\"', '\"', '\"', true, true),
154   new command(&GainAllItems, "gain all items", '$', '$', '$', true, true),
155   new command(&SecretKnowledge, "reveal secret knowledge", '*', '*', '*', true, true),
156   new command(&DetachBodyPart, "detach a limb", '0', '0', '0', true, true),
157   new command(&SetFireToBodyPart, "set fire to a limb", ']', ']', ']', true, true),
158   new command(&SummonMonster, "summon monster", '&', '&', '&', false, true),
159   new command(&LevelTeleport, "level teleport", '|', '|', '|', false, true),
160   new command(&Possess, "possess creature", '{', '{', '{', false, true),
161   new command(&Polymorph, "polymorph", '[', '[', '[', true, true),
162 
163 #endif
164 
165   0 //this is important as an end of array indicator
166 };
167 
168 #ifndef WIZARD
DevConsCmd(character * Char)169 truth commandsystem::DevConsCmd(character* Char)
170 {
171   devcons::OpenCommandsConsole();
172   return false;
173 }
174 #endif
175 
IsForRegionListItem(int iIndex)176 truth commandsystem::IsForRegionListItem(int iIndex){
177   truth (*LinkedFunction)(character*) = Command[iIndex]->GetLinkedFunction();
178 
179   static std::vector<truth (*)(character*)> vLF;
180   static bool bInitDummy = [](){ //for easy maintenance avoiding macros
181     vLF.push_back(&Apply);
182     vLF.push_back(&Dip);
183     vLF.push_back(&Drink);
184     vLF.push_back(&Drop);
185     vLF.push_back(&Eat);
186     vLF.push_back(&WhatToEngrave);
187     vLF.push_back(&EquipmentScreen);
188     vLF.push_back(&Offer);
189     vLF.push_back(&Open);
190     vLF.push_back(&PickUp);
191     vLF.push_back(&Pray);
192     vLF.push_back(&Read);
193     vLF.push_back(&Throw);
194     vLF.push_back(&WieldInLeftArm);
195     vLF.push_back(&WieldInRightArm);
196     vLF.push_back(&Zap);
197 #ifdef WIZARD
198     vLF.push_back(&Polymorph);
199 #endif
200     return true;
201   }();
202   for(int i=0;i<vLF.size();i++){
203     if(vLF[i]==LinkedFunction){
204       return true;
205     }
206   }
207 
208   return false;
209 }
IsForRegionSilhouette(int iIndex)210 truth commandsystem::IsForRegionSilhouette(int iIndex){ //see code generator helper script prepareCmdsDescrCode.sh (use cygwin)
211   truth (*LinkedFunction)(character*) = Command[iIndex]->GetLinkedFunction();
212 
213   static std::vector<truth (*)(character*)> vLF;
214   static bool bInitDummy = [](){ //for easy maintenance avoiding macros
215     vLF.push_back(&Apply);
216     vLF.push_back(&Dip);
217     vLF.push_back(&Drink);
218     vLF.push_back(&Drop);
219     vLF.push_back(&Eat);
220     vLF.push_back(&WhatToEngrave);
221     vLF.push_back(&EquipmentScreen);
222     vLF.push_back(&Offer);
223     vLF.push_back(&Open);
224     vLF.push_back(&PickUp);
225     vLF.push_back(&Pray);
226     vLF.push_back(&Read);
227     vLF.push_back(&ShowInventory);
228     vLF.push_back(&SwapWeaponsCfg);
229     vLF.push_back(&Read);
230     vLF.push_back(&Throw);
231     vLF.push_back(&WieldInLeftArm);
232     vLF.push_back(&WieldInRightArm);
233     vLF.push_back(&Zap);
234 #ifdef WIZARD
235     vLF.push_back(&Polymorph);
236 #endif
237     return true;
238   }();
239   for(int i=0;i<vLF.size();i++){
240     if(vLF[i]==LinkedFunction){
241       return true;
242     }
243   }
244 
245   return false;
246 }
247 
findCmdKey(truth (* func)(character *))248 int findCmdKey(truth (*func)(character*))
249 {
250   int iKey=0;
251   for(int i = 1; command* cmd = commandsystem::GetCommand(i); ++i)
252     if(cmd->GetLinkedFunction()==func){
253       iKey = cmd->GetKey();
254       break;
255     }
256   if(iKey==0)ABORT("can't find key for command."); //TODO how to show what command from *func???
257   return iKey;
258 }
259 
GoUp(character * Char)260 truth commandsystem::GoUp(character* Char)
261 {
262   if(!Char->TryToUnStickTraps(ZERO_V2))
263     return false;
264 
265   /*if(!game::IsInWilderness() && game::WizardModeIsActive() && game::GetCurrentLevelIndex() >= 1)
266     if(game::TryTravel(game::GetCurrentDungeonIndex(), game::GetCurrentLevelIndex() - 1, RANDOM, true))
267     return true;*/
268 
269   oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain();
270 
271   if(!Terrain)
272   {
273     if(game::IsInWilderness())
274     {
275       if(!Char->IsFlying())
276         ADD_MESSAGE("You jump into the air. For some reason you don't get too far above.");
277       else
278         ADD_MESSAGE("You fly around for some time.");
279     }
280     else
281       ADD_MESSAGE("You can't go up.");
282 
283     return false;
284   }
285 
286   if(Terrain->Enter(true))
287   {
288     Char->EditExperience(LEG_STRENGTH, 150, 1 << 6);
289     Char->EditNP(-20);
290     Char->EditAP(-100000 / APBonus(Char->GetAttribute(AGILITY)));
291     return true;
292   }
293   else
294     return false;
295 }
296 
GoDown(character * Char)297 truth commandsystem::GoDown(character* Char)
298 {
299   if(!Char->TryToUnStickTraps(ZERO_V2))
300     return false;
301 
302   /*if(!game::IsInWilderness() && game::WizardModeIsActive() && game::GetCurrentLevelIndex() < game::GetLevels() - 1)
303     if(game::TryTravel(game::GetCurrentDungeonIndex(), game::GetCurrentLevelIndex() + 1, RANDOM, true))
304     return true;*/
305 
306   oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain();
307 
308   if(!Terrain)
309   {
310     if(game::IsInWilderness())
311       ADD_MESSAGE("There seems to be nothing of interest here.");
312     else
313       ADD_MESSAGE("You can't go down.");
314 
315     return false;
316   }
317 
318   if(Terrain->Enter(false))
319   {
320     Char->EditExperience(AGILITY, 150, 1 << 6);
321     Char->EditNP(-10);
322     Char->EditAP(-100000 / APBonus(Char->GetAttribute(AGILITY)));
323     return true;
324   }
325   else
326     return false;
327 }
328 
Open(character * Char)329 truth commandsystem::Open(character* Char)
330 {
331   if(Char->CanOpen())
332   {
333     int Key;
334 
335     if(ivanconfig::GetSmartOpenCloseApply())
336     {
337       std::vector<lsquare*> SquaresWithOpenableItems;
338       std::vector<olterrain*> OpenableOLTerrains;
339       std::vector<olterrain*> AlreadyOpenOLTerrains;
340 
341       for(int d = 0; d < Char->GetExtendedNeighbourSquares(); ++d)
342       {
343         lsquare* Square = Char->GetNeighbourLSquare(d);
344 
345         if(Square)
346         {
347           if(Square->GetStack()->SortedItems(Char, &item::IsOpenable))
348             SquaresWithOpenableItems.push_back(Square);
349 
350           if(Square->GetOLTerrain())
351           {
352             if(Square->GetOLTerrain()->CanBeOpened())
353               OpenableOLTerrains.push_back(Square->GetOLTerrain());
354             else if(Square->GetOLTerrain()->IsOpen())
355               AlreadyOpenOLTerrains.push_back(Square->GetOLTerrain());
356           }
357         }
358       }
359 
360       if(Char->GetStack()->SortedItems(Char, &item::IsOpenable))
361       {
362         if(SquaresWithOpenableItems.empty() && OpenableOLTerrains.empty())
363           Key = 'i';
364         else
365           Key = game::AskForKeyPress(CONST_S("What do you wish to open? "
366                                              "[press a direction key, space or 'i']"));
367 
368         if(Key == 'i')
369         {
370           item* Item = Char->GetStack()->DrawContents(Char,
371                                                       CONST_S("What do you want to open?"),
372                                                       0, &item::IsOpenable);
373           return Item && Item->Open(Char);
374         }
375       }
376       else if(SquaresWithOpenableItems.size() == 1 && OpenableOLTerrains.empty())
377         return SquaresWithOpenableItems[0]->Open(Char);
378       else if(SquaresWithOpenableItems.empty() && OpenableOLTerrains.size() == 1)
379         return OpenableOLTerrains[0]->Open(Char);
380       else if(SquaresWithOpenableItems.empty() && OpenableOLTerrains.empty())
381       {
382         if(!AlreadyOpenOLTerrains.empty())
383           return AlreadyOpenOLTerrains[0]->Open(Char);
384         else
385         {
386           ADD_MESSAGE("Find something to open first, %s.", game::Insult());
387           return false;
388         }
389       }
390       else
391         Key = game::AskForKeyPress(CONST_S("What do you wish to open? "
392                                            "[press a direction key or space]"));
393     }
394     else
395     {
396       truth OpenableItems = Char->GetStack()->SortedItems(Char, &item::IsOpenable);
397 
398       if(OpenableItems)
399         Key = game::AskForKeyPress(CONST_S("What do you wish to open? "
400                                            "[press a direction key, space or 'i']"));
401       else
402         Key = game::AskForKeyPress(CONST_S("What do you wish to open? "
403                                            "[press a direction key or space]"));
404 
405       if(Key == 'i' && OpenableItems)
406       {
407         item* Item = Char->GetStack()->DrawContents(Char,
408                                                     CONST_S("What do you want to open?"),
409                                                     0, &item::IsOpenable);
410         return Item && Item->Open(Char);
411       }
412     }
413 
414     v2 DirVect = game::GetDirectionVectorForKey(Key);
415 
416     if(DirVect != ERROR_V2 && Char->GetArea()->IsValidPos(Char->GetPos() + DirVect)){
417       return Char->GetNearLSquare(Char->GetPos() + DirVect)->Open(Char);
418     }
419   }
420   else
421     ADD_MESSAGE("This monster type cannot open anything.");
422 
423   return false;
424 }
425 
Close(character * Char)426 truth commandsystem::Close(character* Char)
427 {
428   if(Char->CanOpen())
429   {
430     if(ivanconfig::GetSmartOpenCloseApply())
431     {
432       /* See if there's only a single open door nearby, otherwise ask for a direction */
433 
434       int ThingsToClose = 0;
435       int ThingsAlreadyClosed = 0;
436       lsquare* SquareWithThingToClose;
437       lsquare* SquareWithThingAlreadyClosed;
438 
439       for(int d = 0; d < Char->GetExtendedNeighbourSquares(); ++d)
440       {
441         lsquare* Square = Char->GetNeighbourLSquare(d);
442 
443         if(!Square || !Square->GetOLTerrain())
444           continue;
445 
446         if(Square->GetOLTerrain()->IsOpen())
447         {
448           ++ThingsToClose;
449 
450           if(ThingsToClose > 1)
451             break;
452 
453           SquareWithThingToClose = Square;
454         }
455         else if(Square->GetOLTerrain()->IsCloseable())
456         {
457           ++ThingsAlreadyClosed;
458           SquareWithThingAlreadyClosed = Square;
459         }
460       }
461 
462       if(ThingsToClose == 0)
463       {
464         if(ThingsAlreadyClosed == 0)
465           ADD_MESSAGE("Find something to close first, %s.", game::Insult());
466         else
467           SquareWithThingAlreadyClosed->Close(Char);
468       }
469       else if(ThingsToClose == 1)
470         return SquareWithThingToClose->Close(Char);
471       else
472       {
473         int Dir = game::DirectionQuestion(CONST_S("What do you wish to close? [press a direction key]"), false);
474 
475         if(Dir != DIR_ERROR && Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir)))
476           return Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir))->Close(Char);
477       }
478     }
479     else
480     {
481       int Dir = game::DirectionQuestion(CONST_S("What do you wish to close? [press a direction key]"), false);
482 
483       if(Dir != DIR_ERROR && Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir)))
484         return Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir))->Close(Char);
485     }
486   }
487   else
488     ADD_MESSAGE("This monster type cannot close anything.");
489 
490   return false;
491 }
492 
Drop(character * Char)493 truth commandsystem::Drop(character* Char)
494 {
495   if(!Char->GetStack()->GetItems())
496   {
497     ADD_MESSAGE("You have nothing to drop!");
498     return false;
499   }
500 
501   truth Success = false;
502   stack::SetSelected(0);
503 
504   for(;;)
505   {
506     itemvector ToDrop;
507     game::DrawEverythingNoBlit();
508     Char->GetStack()->DrawContents(ToDrop, Char, CONST_S("What do you want to drop?"), REMEMBER_SELECTED);
509 
510     if(ToDrop.empty())
511       break;
512 
513     if(game::IsInWilderness())
514     {
515       for(uint c = 0; c < ToDrop.size(); ++c)
516       {
517         if(game::TruthQuestion(CONST_S("Are you sure? You will never see ") + ToDrop[c]->CHAR_NAME(DEFINITE)
518                                + " again! [y/N]"))
519         {
520 
521           ADD_MESSAGE("You drop %s.", ToDrop[c]->CHAR_NAME(DEFINITE));
522           ToDrop[c]->RemoveFromSlot();
523           ToDrop[c]->SendToHell();
524         }
525       }
526     }
527     else if(!Char->GetRoom() || Char->GetRoom()->DropItem(Char, ToDrop[0], ToDrop.size()))
528     {
529       ADD_MESSAGE("%s dropped.", ToDrop[0]->GetName(INDEFINITE, ToDrop.size()).CStr());
530       for(uint c = 0; c < ToDrop.size(); ++c)
531       {
532         ToDrop[c]->MoveTo(Char->GetStackUnder());
533         if(ivanconfig::IsAutoPickupThrownItems()){
534           ToDrop[c]->ClearTag('t'); //throw: to avoid auto-pickup
535           if(game::IsAutoPickupMatch(ToDrop[c]->GetName(DEFINITE))){
536             ToDrop[c]->SetTag('d'); //intentionally dropped: this will let user decide specific items to NOT auto-pickup regex matching
537           }
538         }
539       }
540       Success = true;
541     }
542   }
543 
544   if(Success)
545   {
546     Char->DexterityAction(2);
547     return true;
548   }
549 
550   return false;
551 }
552 
Eat(character * Char)553 truth commandsystem::Eat(character* Char)
554 {
555   if(!Char->CheckConsume(CONST_S("eat"))){
556     return false;
557   }
558 
559   lsquare* Square = Char->GetLSquareUnder();
560 
561   if(!game::IsInWilderness() && Square->GetOLTerrain() && Square->GetOLTerrain()->HasEatEffect())
562   {
563     if(Square->GetOLTerrain()->Eat(Char)){
564       return true;
565     }
566   }
567 
568   return Consume(Char, "eat", "eating", &item::IsEatable);
569 }
570 
Drink(character * Char)571 truth commandsystem::Drink(character* Char)
572 {
573 
574   if(!Char->CheckConsume(CONST_S("drink"))){
575     return false;
576   }
577 
578   lsquare* Square = Char->GetLSquareUnder();
579 
580   if(!game::IsInWilderness() && Square->GetOLTerrain() && Square->GetOLTerrain()->HasDrinkEffect())
581   {
582     if(Square->GetOLTerrain()->Drink(Char)){
583       return true;
584     }
585   }
586 
587   return Consume(Char, "drink", "drinking", &item::IsDrinkable);
588 }
589 
Taste(character * Char)590 truth commandsystem::Taste(character* Char)
591 {
592   if(!Char->CheckConsume(CONST_S("drink")))
593     return false;
594 
595   lsquare* Square = Char->GetLSquareUnder();
596 
597   if(!game::IsInWilderness() && Square->GetOLTerrain() && Square->GetOLTerrain()->HasDrinkEffect())
598   {
599     if(Square->GetOLTerrain()->Drink(Char))
600       return true;
601   }
602 
603   return Consume(Char, "sip", "sipping", &item::IsDrinkable, true);
604 }
605 
Consume(character * Char,cchar * ConsumeVerb,cchar * ConsumeVerbPresentParticiple,sorter Sorter,truth nibbling)606 truth commandsystem::Consume(character* Char, cchar* ConsumeVerb, cchar* ConsumeVerbPresentParticiple,
607                              sorter Sorter, truth nibbling)
608 {
609   lsquare* Square = Char->GetLSquareUnder();
610   stack* Inventory = Char->GetStack();
611   stack* StackUnder = Square->GetStack();
612 
613   if((game::IsInWilderness() || !StackUnder->SortedItems(Char, Sorter)) && !Inventory->SortedItems(Char, Sorter))
614   {
615     ADD_MESSAGE("You have nothing to %s!", ConsumeVerb);
616     return false;
617   }
618 
619   itemvector Item;
620   festring Question = CONST_S("What do you wish to ") + ConsumeVerb + '?';
621 
622   if(!game::IsInWilderness() && StackUnder->SortedItems(Char, Sorter))
623     Inventory->DrawContents(Item, StackUnder, Char, Question, CONST_S("Items in your inventory"),
624                             CONST_S("Items on the ground"), CONST_S(""), 0, NO_MULTI_SELECT, Sorter);
625   else
626     Inventory->DrawContents(Item, Char, Question, NO_MULTI_SELECT, Sorter);
627 
628   return !Item.empty() ? Char->ConsumeItem(Item[0], ConsumeVerbPresentParticiple, nibbling) : false;
629 }
630 
ShowInventory(character * Char)631 truth commandsystem::ShowInventory(character* Char)
632 {
633   festring Title("Your inventory (total weight: ");
634   Title << Char->GetStack()->GetWeight();
635   Title << "g)";
636   Char->GetStack()->DrawContents(Char, Title, NO_SELECT);
637 
638   return false;
639 }
640 
PickUp(character * Char)641 truth commandsystem::PickUp(character* Char)
642 {
643 
644   if(!Char->GetStackUnder()->GetVisibleItems(Char))
645   {
646     ADD_MESSAGE("There is nothing here to pick up!");
647     return false;
648   }
649 
650   itemvectorvector PileVector;
651   Char->GetStackUnder()->Pile(PileVector, Char, CENTER);
652 
653   for(int c = 0; c < 4; ++c)
654   {
655     stack* Stack = Char->GetLSquareUnder()->GetStackOfAdjacentSquare(c);
656 
657     if(Stack)
658       Stack->Pile(PileVector, Char, 3 - c);
659   }
660 
661   if(PileVector.size() == 1)
662   {
663     if(PileVector[0][0]->CanBePickedUp())
664     {
665       int Amount = PileVector[0].size();
666 
667       if(Amount > 1)
668         Amount = game::ScrollBarQuestion(CONST_S("How many ") + PileVector[0][0]->GetName(PLURAL) + '?',
669                                          Amount, 1, 0, Amount, 0, WHITE, LIGHT_GRAY, DARK_GRAY);
670 
671       if(!Amount){
672         return false;
673       }
674 
675       if((!PileVector[0][0]->GetRoom()
676           || PileVector[0][0]->GetRoom()->PickupItem(Char, PileVector[0][0], Amount))
677          && PileVector[0][0]->CheckPickUpEffect(Char))
678       {
679         for(int c = 0; c < Amount; ++c)
680         {
681           if(ivanconfig::GetRotateTimesPerSquare() > 0)
682             PileVector[0][c]->ResetFlyingThrownStep();
683 
684           PileVector[0][c]->MoveTo(Char->GetStack());
685 
686           if(game::IsAutoPickupMatch(PileVector[0][c]->GetName(DEFINITE))){
687             PileVector[0][c]->ClearTag('d'); //intentionally drop tag dismissed for autopickup regex match
688           }
689         }
690 
691         ADD_MESSAGE("%s picked up.", PileVector[0][0]->GetName(INDEFINITE, Amount).CStr());
692         Char->DexterityAction(2);
693         return true;
694       }
695       else
696       {
697         return false;
698       }
699     }
700     else
701     {
702       ADD_MESSAGE("%s too large to pick up!", PileVector[0].size() == 1 ? "It is" : "They are");
703       return false;
704     }
705   }
706 
707   truth Success = false;
708   stack::SetSelected(0);
709 
710   for(;;)
711   {
712     itemvector ToPickup;
713     game::DrawEverythingNoBlit();
714     Char->GetStackUnder()->DrawContents(ToPickup, Char, CONST_S("What do you want to pick up?"), REMEMBER_SELECTED);
715 
716     if(ToPickup.empty())
717       break;
718 
719     if(ToPickup[0]->CanBePickedUp())
720     {
721       if((!ToPickup[0]->GetRoom()
722           || ToPickup[0]->GetRoom()->PickupItem(Char, ToPickup[0], ToPickup.size()))
723          && ToPickup[0]->CheckPickUpEffect(Char))
724       {
725         for(uint c = 0; c < ToPickup.size(); ++c)
726         {
727           if(ivanconfig::GetRotateTimesPerSquare() > 0)
728             ToPickup[c]->ResetFlyingThrownStep();
729 
730           ToPickup[c]->MoveTo(Char->GetStack());
731 
732           if(game::IsAutoPickupMatch(ToPickup[c]->GetName(DEFINITE))){
733             ToPickup[c]->ClearTag('d'); //intentionally drop tag dismissed for autopickup regex match
734           }
735         }
736 
737         ADD_MESSAGE("%s picked up.", ToPickup[0]->GetName(INDEFINITE, ToPickup.size()).CStr());
738         Success = true;
739       }
740     }
741     else
742       ADD_MESSAGE("%s too large to pick up!", ToPickup.size() == 1 ? "It is" : "They are");
743   }
744 
745   if(Success)
746   {
747     Char->DexterityAction(2);
748     return true;
749   }
750 
751   return false;
752 }
753 
Quit(character * Char)754 truth commandsystem::Quit(character* Char)
755 {
756   if(game::TruthQuestion(CONST_S("Your quest is not yet completed! Really quit? [y/N]")))
757   {
758     Char->ShowAdventureInfo();
759     festring Msg = CONST_S("cowardly quit the game");
760     Char->AddScoreEntry(Msg, 0.75);
761     game::End(Msg, !game::WizardModeIsActive() || game::TruthQuestion(CONST_S("Remove saves? [y/N]")));
762     return true;
763   }
764   else
765     return false;
766 }
767 
Talk(character * Char)768 truth commandsystem::Talk(character* Char)
769 {
770   static int cmdKey = findCmdKey(&Talk);
771 
772   if(!Char->CheckTalk())
773     return false;
774 
775   character* ToTalk = 0;
776   int Characters = 0;
777 
778   for(int d = 0; d < 8; ++d)
779   {
780     lsquare* Square = Char->GetNaturalNeighbourLSquare(d);
781 
782     if(Square)
783     {
784       character* Dude = Square->GetCharacter();
785 
786       if(Dude)
787       {
788         ToTalk = Dude;
789         ++Characters;
790       }
791     }
792   }
793 
794   if(!Characters)
795   {
796     ADD_MESSAGE("Find yourself someone to talk to first!");
797     return false;
798   }
799   else if(Characters == 1)
800     return ToTalk->ChatMenu();
801   else
802   {
803     static festring fsQ;
804     static bool bInitDummy=[](){fsQ<<"To whom do you wish to talk? [press a direction key or '"<<(char)cmdKey<<"']";return true;}();
805     static int iPreviousDirChosen = DIR_ERROR;
806     int Dir = game::DirectionQuestion(fsQ, false, true, cmdKey, iPreviousDirChosen);
807 
808     if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir)))
809       return false;
810 
811     iPreviousDirChosen = Dir;
812     character* Dude = Char->GetNearSquare(Char->GetPos() + game::GetMoveVector(Dir))->GetCharacter();
813 
814     if(Dude == Char)
815     {
816       ADD_MESSAGE("You talk to yourself for some time.");
817       Char->EditExperience(WISDOM, -50, 1 << 7);
818       Char->EditAP(-1000);
819       return true;
820     }
821     else if(Dude)
822       return Dude->ChatMenu();
823     else
824       ADD_MESSAGE("You get no response.");
825   }
826 
827   return false;
828 }
829 
NOP(character * Char)830 truth commandsystem::NOP(character* Char)
831 {
832   Char->EditExperience(DEXTERITY, -25, 1 << 3);
833   Char->EditExperience(AGILITY, -25, 1 << 3);
834   Char->EditAP(-Char->GetStateAPGain(1000));
835   return true;
836 }
837 
Save(character *)838 truth commandsystem::Save(character*)
839 {
840   if(game::TruthQuestion(CONST_S("Do you truly wish to save and flee? [y/N]")))
841   {
842     game::Save();
843     game::SRegionAroundDisable();
844     game::End("", false);
845     return true;
846   }
847   else
848     return false;
849 }
850 
Read(character * Char)851 truth commandsystem::Read(character* Char)
852 {
853   if(!Char->CanRead() && !game::GetSeeWholeMapCheatMode())
854   {
855     ADD_MESSAGE("You can't read.");
856     return false;
857   }
858 
859   if(!Char->GetStack()->SortedItems(Char, &item::IsReadable))
860   {
861     ADD_MESSAGE("You have nothing to read!");
862     return false;
863   }
864 
865   if(Char->GetLSquareUnder()->IsDark() && !game::GetSeeWholeMapCheatMode())
866   {
867     ADD_MESSAGE("It is too dark to read.");
868     return false;
869   }
870 
871   item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to read?"), 0, &item::IsReadable);
872 
873 #ifdef WIZARD
874   // stops auto question timeout that was preventing reading at all
875   if(Item && game::GetAutoPlayMode())
876     game::DisableAutoPlayMode();
877 #endif
878 
879   return Item && Char->ReadItem(Item);
880 }
881 
Dip(character * Char)882 truth commandsystem::Dip(character* Char)
883 {
884 
885   if(!Char->GetStack()->SortedItems(Char, &item::IsDippable) && !Char->EquipsSomething(&item::IsDippable))
886   {
887     ADD_MESSAGE("You have nothing to dip!");
888     return false;
889   }
890 
891   truth HasDipDestination = Char->PossessesItem(&item::IsDipDestination);
892   truth DipDestinationNear = false;
893 
894   for(int d = 0; d < 9; ++d)
895   {
896     lsquare* Square = Char->GetNaturalNeighbourLSquare(d);
897 
898     if(Square && Square->IsDipDestination())
899       DipDestinationNear = true;
900   }
901 
902   if(!HasDipDestination && !DipDestinationNear)
903   {
904     ADD_MESSAGE("There is nothing to dip anything into.");
905     return false;
906   }
907 
908   item* Item = Char->SelectFromPossessions(CONST_S("What do you want to dip?"), &item::IsDippable);
909 
910   if(Item)
911   {
912     if(!HasDipDestination
913        || (DipDestinationNear && game::TruthQuestion(CONST_S("Do you wish to dip in a nearby square? [y/N]"))))
914     {
915       int Dir = game::DirectionQuestion(CONST_S("Where do you want to dip ") + Item->GetName(DEFINITE)
916                                         + "? [press a direction key or '.']", false, true);
917       v2 Pos = Char->GetPos() + game::GetMoveVector(Dir);
918 
919       if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Pos) || !Char->GetNearLSquare(Pos)->IsDipDestination()){
920         return false;
921       }
922 
923       bool b = Char->GetNearLSquare(Pos)->DipInto(Item, Char);
924 
925       return b;
926     }
927     else
928     {
929       item* DipTo = Char->SelectFromPossessions(CONST_S("Into what do you wish to dip it?"), &item::IsDipDestination);
930 
931       if(DipTo)
932       {
933         if(Item == DipTo)
934         {
935           ADD_MESSAGE("Very funny...");
936           return false;
937         }
938 
939         Item->DipInto(DipTo->CreateDipLiquid(Item->DipIntoVolume()), Char);
940         return true;
941       }
942     }
943   }
944 
945   return false;
946 }
947 
ShowKeyLayout(character *)948 truth commandsystem::ShowKeyLayout(character*)
949 {
950   felist List(CONST_S("Keyboard Layout"));
951   List.AddDescription(CONST_S(""));
952   List.AddDescription(CONST_S("Key       Description"));
953   festring Buffer;
954 
955   // Movement keys
956   // TODO: Better way to handle F1 help text. Needs to be assigned only to the first
957   // line because this is non-selectable list, but unfortunately has to be assigned
958   // immediately after setting the first line through SetLastEntryHelp()
959   switch(ivanconfig::GetDirectionKeyMap())
960   {
961    case DIR_NORM: // Normal
962    {
963      List.AddEntry(CONST_S("789       movement (normal)"), LIGHT_GRAY);
964      List.SetLastEntryHelp(festring() << "IVAN uses most of the keyboard for command key bindings, though some "
965                                       << "commands are only accessible in wizard mode. Note that the game "
966                                       << "distinguishes between lowercase and uppercase letters, so if you are "
967                                       << "experiencing troubles, first check whether you don't have active CapsLock.");
968      List.AddEntry(CONST_S("4 6        or use arrow keys and"), LIGHT_GRAY);
969      List.AddEntry(CONST_S("123        Home, End, PgUp, PgDn"), LIGHT_GRAY);
970      break;
971    }
972    case DIR_ALT: // Alternative
973    {
974      List.AddEntry(CONST_S("789       movement (alternative)"), LIGHT_GRAY);
975      List.SetLastEntryHelp(festring() << "IVAN uses most of the keyboard for command key bindings, though some "
976                                       << "commands are only accessible in wizard mode. Note that the game "
977                                       << "distinguishes between lowercase and uppercase letters, so if you are "
978                                       << "experiencing troubles, first check whether you don't have active CapsLock.");
979      List.AddEntry(CONST_S("u o"), LIGHT_GRAY);
980      List.AddEntry(CONST_S("jkl"), LIGHT_GRAY);
981      break;
982    }
983    case DIR_HACK: // Nethack
984    {
985      List.AddEntry(CONST_S("yku       movement (NetHack)"), LIGHT_GRAY);
986      List.SetLastEntryHelp(festring() << "IVAN uses most of the keyboard for command key bindings, though some "
987                                       << "commands are only accessible in wizard mode. Note that the game "
988                                       << "distinguishes between lowercase and uppercase letters, so if you are "
989                                       << "experiencing troubles, first check whether you don't have active CapsLock.");
990      List.AddEntry(CONST_S("h l"), LIGHT_GRAY);
991      List.AddEntry(CONST_S("bjn"), LIGHT_GRAY);
992      break;
993    }
994   }
995 
996   for(int c = 1; GetCommand(c); ++c)
997     if(!GetCommand(c)->IsWizardModeFunction())
998     {
999       Buffer.Empty();
1000       Buffer << game::ToCharIfPossible(GetCommand(c)->GetKey());
1001       Buffer.Resize(10);
1002       List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY);
1003     }
1004 
1005   if(game::WizardModeIsActive())
1006   {
1007     List.AddEntry(CONST_S(""), WHITE);
1008     List.AddEntry(CONST_S("Wizard mode functions:"), WHITE);
1009     List.AddEntry(CONST_S(""), WHITE);
1010 
1011     for(int c = 1; GetCommand(c); ++c)
1012       if(GetCommand(c)->IsWizardModeFunction())
1013       {
1014         Buffer.Empty();
1015         Buffer << game::ToCharIfPossible(GetCommand(c)->GetKey());
1016         Buffer.Resize(10);
1017         List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY);
1018       }
1019   }
1020 
1021   game::SetStandardListAttributes(List);
1022   List.Draw();
1023   return false;
1024 }
1025 
PlayerDiedLookMode(bool bSeeWholeMapCheatMode)1026 void commandsystem::PlayerDiedLookMode(bool bSeeWholeMapCheatMode){
1027   //TODO why this does not work??? if(!PLAYER->IsDead())return;
1028 #ifdef WIZARD
1029   if(bSeeWholeMapCheatMode && !game::GetSeeWholeMapCheatMode()){
1030     game::SeeWholeMap(); //1
1031     game::SeeWholeMap(); //2
1032   }
1033 #endif
1034   commandsystem::Look(PLAYER);
1035 }
1036 
Look(character * Char)1037 truth commandsystem::Look(character* Char)
1038 {
1039   festring Msg; DBG1(Char->GetSquareUnder());
1040   if(!game::IsInWilderness()){
1041     if(Char->GetSquareUnder()==NULL){ //dead (removed) Char (actually PlayerDiedLookMode())
1042       game::GetCurrentLevel()->AddSpecialCursors(); //TODO isnt, this alone, enough?
1043     }else{
1044       Char->GetLevel()->AddSpecialCursors();
1045     }
1046   }
1047 
1048   if(!game::IsInWilderness())
1049     Msg = CONST_S("Direction keys move cursor; examine (i)tems or a (c)haracter; ESC exits.");
1050   else
1051     Msg = CONST_S("Direction keys move cursor; examine a (c)haracter; ESC exits.");
1052 
1053   v2 pos = Char->GetPosSafely();
1054   if(pos.Is0())pos = game::GetCamera()+v2(game::GetScreenXSize(),game::GetScreenYSize())/2; // gum: this may happen if player died, the probably position is around screen center, if it is not good enough just deny it and add a log message saying unable to.
1055   game::PositionQuestion(Msg,pos,&game::LookHandler, &game::LookKeyHandler, ivanconfig::GetLookZoom());
1056   game::RemoveSpecialCursors();
1057   return false;
1058 }
1059 
WhatToEngrave(character * Char)1060 truth commandsystem::WhatToEngrave(character* Char)
1061 {
1062   return WhatToEngrave(Char,false,v2());
1063 }
WhatToEngrave(character * Char,bool bEngraveMapNote,v2 v2EngraveMapNotePos)1064 truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2EngraveMapNotePos)
1065 {
1066   if(!Char->CanRead())
1067   {
1068     ADD_MESSAGE("You can't even read.");
1069     return false;
1070   }
1071 
1072   int Key = 0;
1073   while(!(Key == KEY_ESC || Key == ' '))
1074   {
1075     if(!bEngraveMapNote)
1076       Key = game::AskForKeyPress(CONST_S("Do you want to (.) engrave a square, or inscribe an (i)tem? ['.' or 'i', ESC exits]"));
1077 
1078     int iLSqrLimit=80;
1079     if(bEngraveMapNote)
1080     {
1081       festring What;
1082 
1083       lsquare* lsqrN = game::GetCurrentLevel()->GetLSquare(v2EngraveMapNotePos);
1084       if(lsqrN!=NULL){ //TODO can this ever be NULL?
1085         if(lsqrN->GetEngraved()!=NULL){
1086           cchar* c = lsqrN->GetEngraved();
1087           if(c!=NULL){
1088             What=c;
1089             if(What.GetSize()>0){
1090               if(What[0]==game::MapNoteToken()){ //having map note token means it is already a map note, so let it be read/write at will
1091                 std::string str=What.CStr();
1092                 What.Empty();
1093                 What<<str.substr(1).c_str(); //removes token prefix TODO implement substr() at festring
1094               } else { // this is a case of non-map note "engraving" (like normal vanilla engraving from golemns)
1095                 if(!lsqrN->HasBeenSeen()){
1096                   /*****
1097                    * why empty it?
1098                    * to prevent determining (cheating) if that square has a golemn using the ShowMap/lookMode command! (possibly other things too)
1099                    * a minor problem is that the user may overwrite that engraving with a mapnote TODO something about it?
1100                    */
1101                   What.Empty();
1102                 }
1103               }
1104             }
1105           }
1106         }
1107       }
1108 
1109       if(game::StringQuestion(What, CONST_S("Write your map note (optionally position mouse cursor over it before editing):"), WHITE, 0, iLSqrLimit, true) == NORMAL_EXIT){
1110         if(What.GetSize()>0) {
1111           game::SetMapNote(lsqrN,What);
1112         }
1113       }
1114 
1115       break;
1116     }
1117 
1118     if(Key == '.')
1119     {
1120       festring What;
1121 
1122       if(game::StringQuestion(What, CONST_S("What do you want to engrave here?"), WHITE, 0, iLSqrLimit, true) == NORMAL_EXIT)
1123         Char->GetNearLSquare(Char->GetPos())->Engrave(What);
1124 
1125       break;
1126     }
1127 
1128     if(Key == 'i')
1129     {
1130       if(!Char->GetStack()->GetItems())
1131       {
1132         ADD_MESSAGE("You have nothing to inscribe on!");
1133         return false;
1134       }
1135 
1136       stack::SetSelected(0);
1137 
1138       for(;;)
1139       {
1140         itemvector ToAddLabel;
1141         game::DrawEverythingNoBlit();
1142         Char->GetStack()->DrawContents(ToAddLabel, Char, CONST_S("What item do you want to inscribe on?"), REMEMBER_SELECTED);
1143 
1144         if(ToAddLabel.empty())
1145           break;
1146 
1147         festring What = ToAddLabel[0]->GetLabel();
1148         if(game::StringQuestion(What, CONST_S("What would you like to inscribe on this item?"), WHITE, 0, 20, true) == NORMAL_EXIT)
1149           for(int i=0;i<ToAddLabel.size();i++)
1150             ToAddLabel[i]->SetLabel(What);
1151       }
1152 
1153       break;
1154     }
1155   }
1156 
1157   return false;
1158 }
1159 
Pray(character * Char)1160 truth commandsystem::Pray(character* Char)
1161 {
1162   felist Panthenon(CONST_S("To Whom you want to address your prayers?"));
1163   Panthenon.SetEntryDrawer(game::GodEntryDrawer);
1164   int Known[GODS];
1165   int Index = 0;
1166   int DivineMaster = Char->GetLSquareUnder()->GetDivineMaster();
1167 
1168   if(DivineMaster == ATHEIST)
1169   {
1170     ADD_MESSAGE("Somehow you feel that no god will help you here.");
1171     return false;
1172   }
1173 
1174   if(!DivineMaster)
1175   {
1176     festring desc;
1177     for(int c = 1; c <= GODS; ++c)
1178       if(game::GetGod(c)->IsKnown())
1179       {
1180         desc.Empty();
1181         desc << game::GetGod(c)->GetCompleteDescription();
1182         if(ivanconfig::IsShowGodInfo())desc << " " << game::GetGod(c)->GetLastKnownRelation();
1183         Panthenon.AddEntry(desc, LIGHT_GRAY, 20, c);
1184         Panthenon.SetLastEntryHelp(festring() << game::GetGod(c)->GetName() << ", the " << game::GetGod(c)->GetDescription());
1185         Known[Index++] = c;
1186       }
1187   }
1188   else
1189     if(game::GetGod(DivineMaster)->IsKnown())
1190     {
1191       Panthenon.AddEntry(game::GetGod(DivineMaster)->GetCompleteDescription(), LIGHT_GRAY, 20, DivineMaster);
1192       Panthenon.SetLastEntryHelp(festring() << game::GetGod(DivineMaster)->GetName() << ", the " << game::GetGod(DivineMaster)->GetDescription());
1193       Known[0] = DivineMaster;
1194     }
1195     else
1196     {
1197       ADD_MESSAGE("Somehow you feel that no deity you know can hear your prayers from this place.");
1198       return false;
1199     }
1200 
1201   game::SetStandardListAttributes(Panthenon);
1202   Panthenon.AddFlags(SELECTABLE);
1203   int Select = Panthenon.Draw();
1204 
1205   if(Select == LIST_WAS_EMPTY)
1206   {
1207     ADD_MESSAGE("You do not know any gods.");
1208     return false;
1209   }
1210 
1211   if(Select & FELIST_ERROR_BIT)
1212     return false;
1213   else
1214   {
1215     if(Char->GetLSquareUnder()->GetDivineMaster())
1216     {
1217       if(!Select)
1218       {
1219         if(game::TruthQuestion(CONST_S("Do you really wish to pray to ")
1220                                + game::GetGod(Char->GetLSquareUnder()->GetDivineMaster())->GetName() + "? [y/N]"))
1221           game::GetGod(Char->GetLSquareUnder()->GetDivineMaster())->Pray();
1222         else
1223           return false;
1224       }
1225       else
1226         return false;
1227     }
1228     else
1229     {
1230       if(game::TruthQuestion(CONST_S("Do you really wish to pray to ")
1231                              + game::GetGod(Known[Select])->GetName() + "? [y/N]"))
1232       {
1233         if(Char->StateIsActivated(CONFUSED) && !(RAND() & 7))
1234         {
1235           int Index;
1236           for(Index = 1 + RAND() % GODS;
1237               Index == Known[Select];
1238               Index = 1 + RAND() % GODS);
1239 
1240           if(game::GetGod(Index)->IsKnown())
1241             ADD_MESSAGE("You feel something went wrong in the rituals. You have "
1242                         "accidentally prayed to %s!", game::GetGod(Index)->GetName());
1243           else
1244             ADD_MESSAGE("Your rituals were seriously incorrect. You have accidentally "
1245                         "prayed to an unknown god, but have no idea how!");
1246 
1247           game::GetGod(Index)->Pray();
1248         }
1249         else
1250           game::GetGod(Known[Select])->Pray();
1251       }
1252       else
1253         return false;
1254     }
1255 
1256     Char->EditAP(-1000);
1257     return true;
1258   }
1259 }
1260 
Kick(character * Char)1261 truth commandsystem::Kick(character* Char)
1262 {
1263   static int cmdKey = findCmdKey(&Kick);
1264 
1265   /** No multi-tile support */
1266 
1267   if(!Char->CheckKick())
1268     return false;
1269 
1270   if(Char->GetBurdenState() == OVER_LOADED)
1271   {
1272     ADD_MESSAGE("You try to kick, but you collapse under your load.");
1273     Char->EditAP(-100000 / APBonus(Char->GetAttribute(AGILITY)));
1274     return true;
1275   }
1276 
1277   static festring fsQ;
1278   static bool bInitDummy=[](){fsQ<<"In what direction do you wish to kick? [press a direction key or '"<<(char)cmdKey<<"']";return true;}();
1279   static int iPreviousDirChosen = DIR_ERROR;
1280   int Dir = game::DirectionQuestion(fsQ, false, false, cmdKey, iPreviousDirChosen);
1281 
1282   if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir)))
1283     return false;
1284 
1285   iPreviousDirChosen = Dir;
1286   lsquare* Square = Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir));
1287 
1288   if(!Square->CheckKick(Char))
1289     return false;
1290 
1291   character* Enemy = Square->GetCharacter();
1292 
1293   if(Enemy && !(Enemy->IsMasochist() && Char->GetRelation(Enemy) == FRIEND) && Char->GetRelation(Enemy) != HOSTILE
1294      && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
1295     return false;
1296 
1297   /*if(Square->GetCharacter() && Char->GetRelation(Square->GetCharacter()) != HOSTILE)
1298     if(!game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
1299       return false;
1300     else
1301       */
1302 
1303   Char->Hostility(Square->GetCharacter());
1304   Char->Kick(Square, Dir);
1305   return true;
1306 }
1307 
Offer(character * Char)1308 truth commandsystem::Offer(character* Char)
1309 {
1310   if(!Char->CheckOffer())
1311     return false;
1312 
1313   lsquare* Square = Char->GetLSquareUnder();
1314 
1315   if(Square->GetOLTerrain() && Square->GetOLTerrain()->AcceptsOffers())
1316   {
1317     if(!Char->GetStack()->GetItems())
1318     {
1319       ADD_MESSAGE("You have nothing to offer!");
1320       return false;
1321     }
1322 
1323     item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to offer?"), REMEMBER_SELECTED);
1324 
1325     if(Item)
1326     {
1327       if(game::GetGod(Square->GetDivineMaster())->ReceiveOffer(Item))
1328       {
1329         Item->RemoveFromSlot();
1330         Item->SendToHell();
1331         Char->DexterityAction(5); /** C **/
1332         return true;
1333       }
1334       else
1335         return false;
1336     }
1337     else
1338       return false;
1339   }
1340   else
1341     ADD_MESSAGE("There isn't any altar here!");
1342 
1343   return false;
1344 }
1345 
DrawMessageHistory(character *)1346 truth commandsystem::DrawMessageHistory(character*)
1347 {
1348   msgsystem::DrawMessageHistory();
1349   return false;
1350 }
1351 
Throw(character * Char)1352 truth commandsystem::Throw(character* Char)
1353 {
1354   static int cmdKey = findCmdKey(&Throw);
1355 
1356   if(!Char->CheckThrow()){
1357     return false;
1358   }
1359 
1360   if(!Char->GetStack()->GetItems())
1361   {
1362     ADD_MESSAGE("You have nothing to throw!");
1363     return false;
1364   }
1365 
1366   item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to throw?"), REMEMBER_SELECTED);
1367 
1368   if(Item)
1369   {
1370     static int iPreviousDirChosen = DIR_ERROR;
1371     int Answer = game::DirectionQuestion(CONST_S("In what direction do you wish to throw?  "
1372                                                  "[press a direction key or Enter]"), false, false, KEY_ENTER, iPreviousDirChosen);
1373 
1374     if(Answer == DIR_ERROR){
1375       return false;
1376     }
1377 
1378     iPreviousDirChosen = Answer;
1379 
1380     Char->ThrowItem(Answer, Item);
1381     Char->EditExperience(ARM_STRENGTH, 75, 1 << 8);
1382     Char->EditExperience(DEXTERITY, 75, 1 << 8);
1383     Char->EditExperience(PERCEPTION, 75, 1 << 8);
1384     Char->EditNP(-50);
1385     Char->DexterityAction(5);
1386     if(ivanconfig::IsAutoPickupThrownItems())
1387       if(Item->IsWeapon(PLAYER)) //TODO never made much sense auto-picking up other things than weapons, but this could be optional
1388         Item->SetTag('t');
1389     return true;
1390   }
1391   else
1392   {
1393     return false;
1394   }
1395 }
1396 
1397 ulong itLastApplyID=0; //save it?
ApplyAgain(character * Char)1398 truth commandsystem::ApplyAgain(character* Char)
1399 {
1400   if(itLastApplyID==0){
1401     ADD_MESSAGE("You need to apply something first, %s.", game::Insult());
1402     return false;
1403   }
1404 
1405   item* it=game::SearchItem(itLastApplyID);
1406   if(!it){
1407     itLastApplyID=0;
1408     ADD_MESSAGE("You cannot apply something that was destroyed, %s.", game::Insult());
1409     return false;
1410   }
1411 
1412   if(it->FindCarrier()==Char){
1413     //ADD_MESSAGE("You will apply your %s again.",it->GetName(UNARTICLED).CStr());
1414     return ApplyWork(Char,it); // There is already a message from the applied item, no? --red_kangaroo
1415   }else
1416     ADD_MESSAGE("You need to get your %s back!",it->GetName(UNARTICLED).CStr());
1417 
1418   return false;
1419 }
1420 
Apply(character * Char)1421 truth commandsystem::Apply(character* Char)
1422 {
1423   return ApplyWork(Char);
1424 }
1425 
ApplyWork(character * Char,item * itOverride)1426 truth commandsystem::ApplyWork(character* Char,item* itOverride)
1427 {
1428   if(!Char->CanApply())
1429   {
1430     ADD_MESSAGE("This monster type cannot apply.");
1431     return false;
1432   }
1433 
1434   if(!Char->CheckApply()){
1435     return false;
1436   }
1437 
1438   if(!Char->PossessesItem(&item::IsAppliable))
1439   {
1440     ADD_MESSAGE("You have nothing to apply!");
1441     return false;
1442   }
1443 
1444   item* Item = itOverride;
1445   if(Item==NULL)
1446     Item = Char->SelectFromPossessions(CONST_S("What do you want to apply?"), &item::IsAppliable);
1447   bool b = Item && Item->Apply(Char);
1448 
1449   if(b)
1450     itLastApplyID=Item->GetID();
1451 
1452   return b;
1453 }
1454 
ForceVomit(character * Char)1455 truth commandsystem::ForceVomit(character* Char)
1456 {
1457   if(Char->CanForceVomit())
1458   {
1459     int Dir = game::DirectionQuestion(CONST_S("Where do you wish to vomit?  [press a direction key]"), false, true);
1460 
1461     if(Dir != DIR_ERROR)
1462     {
1463       v2 VomitPos = Char->GetPos() + game::GetMoveVector(Dir);
1464 
1465       if(Char->GetArea()->IsValidPos(VomitPos))
1466       {
1467         ccharacter* Other = Char->GetArea()->GetSquare(VomitPos)->GetCharacter();
1468 
1469         if(Other && Other->GetTeam() != Char->GetTeam()
1470            && Other->GetRelation(Char) != HOSTILE
1471            && Other->CanBeSeenBy(Char)
1472            && !game::TruthQuestion("Do you really want to vomit at " + Other->GetObjectPronoun() + "? [y/N]"))
1473            return false;
1474 
1475         ADD_MESSAGE(Char->GetForceVomitMessage().CStr());
1476         Char->Vomit(Char->GetPos() + game::GetMoveVector(Dir), 500 + RAND() % 500, false);
1477         Char->EditAP(-1000);
1478         return true;
1479       }
1480     }
1481   }
1482   else
1483     ADD_MESSAGE("You can't vomit.");
1484 
1485   return false;
1486 }
1487 
Zap(character * Char)1488 truth commandsystem::Zap(character* Char)
1489 {
1490 
1491   if(!Char->CheckZap()){
1492     return false;
1493   }
1494 
1495   if(!Char->PossessesItem(&item::IsZappable))
1496   {
1497     ADD_MESSAGE("You have nothing to zap with, %s.", game::Insult());
1498     return false;
1499   }
1500 
1501   item* Item = Char->SelectFromPossessions(CONST_S("What do you want to zap with?"), &item::IsZappable);
1502 
1503   if(Item)
1504   {
1505     int Answer = game::DirectionQuestion(CONST_S("In what direction do you wish to zap?  "
1506                                                  "[press a direction key or '.']"), false, true);
1507 
1508     if(Answer == DIR_ERROR){
1509       return false;
1510     }
1511 
1512     if(Item->Zap(Char, Char->GetPos(), Answer))
1513     {
1514       Char->EditAP(-100000 / APBonus(Char->GetAttribute(PERCEPTION)));
1515       return true;
1516     }
1517     else
1518     {
1519       return false;
1520     }
1521   }
1522   else
1523   {
1524     return false;
1525   }
1526 }
1527 
Rest(character * Char)1528 truth commandsystem::Rest(character* Char)
1529 {
1530   if(Char->StateIsActivated(PANIC))
1531   {
1532     ADD_MESSAGE("You are too scared to rest.");
1533     return false;
1534   }
1535 
1536   truth Error = false;
1537 
1538   if(Char->GetHP() == Char->GetMaxHP())
1539   {
1540     ADD_MESSAGE("You HP is already at its maximum.");
1541     Error = true;
1542   }
1543   else if(!Char->CanHeal())
1544   {
1545     ADD_MESSAGE("You cannot heal.");
1546     Error = true;
1547   }
1548 
1549   if(Error)
1550   {
1551     long MinutesToRest = game::NumberQuestion(CONST_S("How many minutes to wait?"), WHITE, true);
1552 
1553     if(MinutesToRest > 0)
1554     {
1555       oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain();
1556 
1557       if(Terrain)
1558         Terrain->ShowRestMessage(Char);
1559 
1560       rest* Rest = rest::Spawn(Char);
1561       Rest->SetMinToStop(game::GetTotalMinutes() + MinutesToRest);
1562       Rest->SetGoalHP(0);
1563       Char->SetAction(Rest);
1564       return true;
1565     }
1566     else
1567       return false;
1568   }
1569 
1570   long HPToRest = game::ScrollBarQuestion(CONST_S("How many hit points you desire?"),
1571                                           Char->GetMaxHP(), 1, 0, Char->GetMaxHP(), 0,
1572                                           WHITE, LIGHT_GRAY, DARK_GRAY);
1573 
1574   if(HPToRest <= Char->GetHP())
1575   {
1576     if(HPToRest != 0)
1577       ADD_MESSAGE("Your HP is already %d.", Char->GetHP());
1578 
1579     return false;
1580   }
1581 
1582   oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain();
1583 
1584   if(Terrain)
1585     Terrain->ShowRestMessage(Char);
1586 
1587   rest* Rest = rest::Spawn(Char);
1588   Rest->SetMinToStop(0);
1589   Rest->SetGoalHP(HPToRest);
1590   Char->SetAction(Rest);
1591   return true;
1592 }
1593 
ShowMap(character * Char)1594 truth commandsystem::ShowMap(character* Char)
1595 {
1596   return ShowMapWork(Char);
1597 }
ShowMapWork(character * Char,v2 * pv2ChoseLocation)1598 truth commandsystem::ShowMapWork(character* Char,v2* pv2ChoseLocation)
1599 {
1600   static humanoid* h;h = dynamic_cast<humanoid*>(PLAYER);
1601 
1602   bool bChoseLocationMode = pv2ChoseLocation!=NULL;
1603 
1604   festring fsHelp;fsHelp<<
1605     "[Map Help:]\n"
1606     " F1 - show this message\n"
1607     " Map notes containing '!' or '!!' will be highlighted.\n"
1608     " Position mouse cursor over a map note to edit or delete it.\n"
1609     " In look mode, clicking on a map note will navigate to that location.\n";
1610 
1611   if(bChoseLocationMode)
1612     if(!game::ToggleShowMapNotes())
1613       game::ToggleShowMapNotes();
1614 
1615   if( h && (h->GetLeftArm() || h->GetRightArm()) ){
1616     if(game::ToggleDrawMapOverlay()){
1617       lsquare* lsqrH=NULL;
1618       while(true){
1619         v2 noteAddPos = Char->GetPos();
1620 
1621         int key;
1622         if(bChoseLocationMode)
1623           key='l';
1624         else
1625           key = game::KeyQuestion(CONST_S("Cartography notes action: (t)oggle, (e)dit/add, (l)ook mode, (r)otate, (d)elete. [press F1 for help]"), //TODO KeyQuestion() should detect F1 and return a default answer, currently F1 will just override any other key press
1626             KEY_ESC, 5, 't', 'l', 'r', 'd', 'e');
1627 
1628         if(specialkeys::IsRequestedEvent(specialkeys::FocusedElementHelp)){
1629           specialkeys::ConsumeEvent(specialkeys::FocusedElementHelp,fsHelp);
1630           continue;
1631         }
1632 
1633         switch(key){
1634           case 'd':
1635             lsqrH = game::GetHighlightedMapNoteLSquare();
1636             if(lsqrH!=NULL){
1637               lsqrH->Engrave(festring());
1638               game::RefreshDrawMapOverlay();
1639             }
1640             continue;
1641           case 'r':
1642             game::RotateMapNotes();
1643             continue;
1644           case 't':
1645             if(game::ToggleShowMapNotes())
1646               ADD_MESSAGE("Let me see my map notes...");
1647             continue;
1648           case 'l':
1649             if(noteAddPos==Char->GetPos()){
1650               game::RefreshDrawMapOverlay();
1651 
1652               festring fsMsg = pv2ChoseLocation!=NULL ? "Chose a location." :
1653                 "Where do you wish to add a map note?";
1654               fsMsg<<" [direction keys move cursor, space accepts]";
1655 
1656               v2 start;
1657               if(pv2ChoseLocation!=NULL){
1658                 if(!(*pv2ChoseLocation).Is0())
1659                   if(Char->GetLevel()->IsValidPos((*pv2ChoseLocation)))
1660                     start=(*pv2ChoseLocation);
1661               }
1662               if(start.Is0())
1663                 start=Char->GetPos();
1664 
1665               if(!game::GetCurrentArea()->IsValidPos(start)){
1666                 // very rare case when opening the map will crash at game::PositionQuestion(,start,...) ... area::GetSquare(start)
1667                 DBG4("CrashWorkaround",DBGAV2(start),game::GetCurrentArea()->GetXSize(),game::GetCurrentArea()->GetYSize());
1668                 DBGBREAKPOINT;
1669                 game::ToggleDrawMapOverlay(); //TODO hint something to the player like internal error was avoided and should just retry?
1670                 return false;
1671               }
1672 
1673               noteAddPos = game::PositionQuestion(fsMsg, start, NULL, NULL, true); DBGSV2(noteAddPos);
1674               if(noteAddPos==ERROR_V2){
1675                 game::ToggleDrawMapOverlay();
1676                 return false; //continue;
1677               }
1678               if(pv2ChoseLocation!=NULL){
1679                 (*pv2ChoseLocation)=noteAddPos;
1680                 game::ToggleDrawMapOverlay();
1681                 return (*pv2ChoseLocation) != Char->GetPos();
1682               }
1683             }
1684             /* no break */
1685           case 'e':
1686             if(noteAddPos==Char->GetPos()){
1687               lsqrH = game::GetHighlightedMapNoteLSquare();
1688               if(lsqrH!=NULL)
1689                 noteAddPos=lsqrH->GetPos();
1690             }
1691             WhatToEngrave(Char,true,noteAddPos);
1692             game::RefreshDrawMapOverlay();
1693             continue;
1694         }
1695         break;
1696       }
1697 
1698       game::ToggleDrawMapOverlay();
1699     }
1700   }else{
1701     ADD_MESSAGE("You can't hold the map!");
1702   }
1703 
1704   return true;
1705 }
1706 
Sit(character * Char)1707 truth commandsystem::Sit(character* Char)
1708 {
1709   lsquare* Square = Char->GetLSquareUnder();
1710   return (Square->GetOLTerrain() && Square->GetOLTerrain()->SitOn(Char)) || Square->GetGLTerrain()->SitOn(Char);
1711 }
1712 
1713 std::vector<v2> RouteGoOn;
1714 level* LevelRouteGoOn=NULL;
1715 v2 v2RouteTarget=v2(0,0); //TODO savegame this?
1716 
GetRouteGoOnCopy()1717 std::vector<v2> commandsystem::GetRouteGoOnCopy(){
1718   if(game::GetCurrentLevel()!=LevelRouteGoOn || v2RouteTarget.Is0()){
1719     std::vector<v2> empty;
1720     return empty;
1721   }
1722   return RouteGoOn;
1723 }
1724 
Go(character * Char)1725 truth commandsystem::Go(character* Char)
1726 {
1727   int Dir = DIR_ERROR;
1728 
1729   if(LevelRouteGoOn!=Char->GetLevel())
1730     v2RouteTarget=v2(0,0);
1731 
1732   if(Char->GetPos()==v2RouteTarget) //TODO is near by 1 dist (2 or more may have a wall in-between)
1733     v2RouteTarget=v2(0,0);
1734 
1735   if(!v2RouteTarget.Is0()){
1736     switch(game::KeyQuestion(CONST_S("Continue going thru the route? [y/n]"), KEY_ESC, 2, 'y', 'n')){
1737       case 'y':
1738         Dir = YOURSELF;
1739         break;
1740       case 'n':
1741         v2RouteTarget=v2(0,0);
1742         break;
1743       default:
1744         return false;
1745     }
1746   }
1747 
1748   if(Dir == DIR_ERROR)
1749     Dir = game::DirectionQuestion(CONST_S("In what direction do you want to go? [press a direction key or '.' for map route]"), false, true);
1750 
1751   if(Dir == DIR_ERROR)
1752     return false;
1753 
1754   RouteGoOn.clear();
1755   if(Dir == YOURSELF){
1756     if(v2RouteTarget.Is0())
1757       if(!ShowMapWork(Char,&v2RouteTarget)){
1758         v2RouteTarget=v2(0,0);
1759         return false;
1760       }
1761 
1762     if(Char->GetPos()==v2RouteTarget){
1763       v2RouteTarget=v2(0,0);
1764       return false;
1765     }
1766 
1767     std::set<v2> Illegal;
1768     node* Node = Char->GetLevel()->FindRoute(Char->GetPos(), v2RouteTarget, Illegal, 0, Char);
1769     if(Node){
1770       RouteGoOn.clear();
1771       while(Node->Last)
1772       {
1773         RouteGoOn.push_back(Node->Pos);
1774         Node = Node->Last;
1775       }
1776     }
1777   }
1778 
1779   if(Dir == YOURSELF && RouteGoOn.size()==0){
1780     v2RouteTarget=v2(0,0);
1781     return false;
1782   }
1783 
1784   go* Go = go::Spawn(Char);
1785   if(Dir == YOURSELF){
1786     Go->SetRoute(RouteGoOn);
1787     Go->SetDirectionFromRoute();
1788     Go->SetIsWalkingInOpen(true); //prevents stopping on path crosses/forks
1789     LevelRouteGoOn=Char->GetLevel();
1790   }else{
1791     Go->SetDirection(Dir);
1792 
1793     int OKDirectionsCounter = 0;
1794 
1795     for(int d = 0; d < Char->GetNeighbourSquares(); ++d)
1796     {
1797       lsquare* Square = Char->GetNeighbourLSquare(d);
1798 
1799       if(Square && Char->CanMoveOn(Square))
1800         ++OKDirectionsCounter;
1801     }
1802 
1803     Go->SetIsWalkingInOpen(OKDirectionsCounter > 2);
1804   }
1805 
1806   Char->SetAction(Go);
1807   Char->EditAP(Char->GetStateAPGain(100)); // gum solution
1808   Char->GoOn(Go, true);
1809   return truth(Char->GetAction());
1810 }
1811 
ShowConfigScreen(character *)1812 truth commandsystem::ShowConfigScreen(character*)
1813 {
1814   configsystem::Load(); // some fields may be too big to be edited from the game interface ex.: autopickup regex would require multiline editing otherwise would not work. TODO should this be optional?
1815   ivanconfig::Show();
1816   return false;
1817 }
1818 
AssignName(character *)1819 truth commandsystem::AssignName(character*)
1820 {
1821   game::NameQuestion();
1822   return false;
1823 }
1824 
EquipmentScreen(character * Char)1825 truth commandsystem::EquipmentScreen(character* Char)
1826 {
1827   return Char->EquipmentScreen(Char->GetStack(), 0);
1828 }
1829 
ScrollMessagesDown(character *)1830 truth commandsystem::ScrollMessagesDown(character*)
1831 {
1832   msgsystem::ScrollDown();
1833   return false;
1834 }
1835 
ScrollMessagesUp(character *)1836 truth commandsystem::ScrollMessagesUp(character*)
1837 {
1838   msgsystem::ScrollUp();
1839   return false;
1840 }
1841 
ShowWeaponSkills(character * Char)1842 truth commandsystem::ShowWeaponSkills(character* Char)
1843 {
1844   felist List(CONST_S("Your experience in weapon categories"));
1845   List.AddDescription(CONST_S(""));
1846   List.AddDescription(CONST_S("Category name                 Level     Points    Needed    Battle bonus"));
1847   truth Something = false;
1848   festring Buffer;
1849 
1850   for(int c = 0; c < Char->GetAllowedWeaponSkillCategories(); ++c)
1851   {
1852     cweaponskill* Skill = Char->GetCWeaponSkill(c);
1853 
1854     if(Skill->GetHits() / 100 || (Char->IsUsingWeaponOfCategory(c)))
1855     {
1856       Buffer = Skill->GetName(c);
1857       Buffer.Resize(30);
1858       Buffer << Skill->GetLevel();
1859       Buffer.Resize(40);
1860       Buffer << Skill->GetHits() / 100;
1861       Buffer.Resize(50);
1862 
1863       if(Skill->GetLevel() != 20)
1864         Buffer << (Skill->GetLevelMap(Skill->GetLevel() + 1) - Skill->GetHits()) / 100;
1865       else
1866         Buffer << '-';
1867 
1868       Buffer.Resize(60);
1869       Buffer << '+' << (Skill->GetBonus() - 1000) / 10;
1870 
1871       if(Skill->GetBonus() % 10)
1872         Buffer << '.' << Skill->GetBonus() % 10;
1873 
1874       Buffer << '%';
1875 
1876       if(Char->IsUsingWeaponOfCategory(c))
1877         List.AddEntry(Buffer, WHITE);
1878       else
1879         List.AddEntry(Buffer, LIGHT_GRAY);
1880 
1881       Something = true;
1882 
1883       List.SetLastEntryHelp(festring() << "Your proficiency with individual weapon categories and accustomization "
1884                                        << "with the currently wielded weapons. Note that should you neglect training "
1885                                        << "your skills for too long, they might start slowly decreasing in level.");
1886     }
1887   }
1888 
1889   if(Char->AddSpecialSkillInfo(List))
1890     Something = true;
1891 
1892   if(Something)
1893   {
1894     game::SetStandardListAttributes(List);
1895     List.Draw();
1896   }
1897   else
1898     ADD_MESSAGE("You are not experienced in any weapon skill yet!");
1899 
1900   return false;
1901 }
1902 
WieldInRightArm(character * Char)1903 truth commandsystem::WieldInRightArm(character* Char)
1904 {
1905   if(!Char->CanUseEquipment())
1906     ADD_MESSAGE("You cannot wield anything.");
1907   else if(Char->TryToChangeEquipment(Char->GetStack(), 0, RIGHT_WIELDED_INDEX))
1908   {
1909     Char->DexterityAction(5);
1910     return true;
1911   }
1912 
1913   return false;
1914 }
1915 
WieldInLeftArm(character * Char)1916 truth commandsystem::WieldInLeftArm(character* Char)
1917 {
1918 
1919   if(!Char->CanUseEquipment())
1920     ADD_MESSAGE("You cannot wield anything.");
1921   else if(Char->TryToChangeEquipment(Char->GetStack(), 0, LEFT_WIELDED_INDEX))
1922   {
1923     Char->DexterityAction(5);
1924     return true;
1925   }
1926 
1927   return false;
1928 }
1929 
Search(character * Char)1930 truth commandsystem::Search(character* Char)
1931 {
1932   Char->Search(Char->GetAttribute(PERCEPTION) << 2);
1933   return true;
1934 }
1935 
ShowWorldSeed(character *)1936 truth commandsystem::ShowWorldSeed(character*)
1937 {
1938   int Seed = game::GetWorldMap()->GetWorldSeed();
1939   if(!Seed)
1940     ADD_MESSAGE("World seed is 0");
1941   else
1942     ADD_MESSAGE("World seed is %d", Seed);
1943 
1944   return false;
1945 }
1946 
1947 #ifdef WIZARD
1948 
WizardMode(character * Char)1949 truth commandsystem::WizardMode(character* Char)
1950 {
1951   if(!game::WizardModeIsActive())
1952   {
1953     if(game::TruthQuestion(CONST_S("Do you want to cheat, cheater? This action cannot be undone. [y/N]")))
1954     {
1955       game::ActivateWizardMode();
1956       ADD_MESSAGE("Wizard mode activated.");
1957 
1958       if(game::IsInWilderness())
1959       {
1960         for(uint j = 0; j < game::GetWorldMap()->GetWasPlaced().size(); j++)
1961         {
1962           owterrain* NewPlace = protocontainer<owterrain>::GetProto(game::GetWorldMap()->GetWasPlaced()[j].X)->Spawn();
1963           v2 LocationPosition = game::GetWorldMap()->GetEntryPos(0, game::GetWorldMap()->GetWasPlaced()[j].Y);
1964           game::GetWorldMap()->GetWSquare(LocationPosition)->ChangeOWTerrain(NewPlace);
1965           game::GetWorldMap()->RevealEnvironment(LocationPosition, 1);
1966         }
1967 
1968         game::GetWorldMap()->SendNewDrawRequest();
1969       }
1970       else
1971       {
1972         game::LoadWorldMap();
1973 
1974         for(uint j = 0; j < game::GetWorldMap()->GetWasPlaced().size(); j++)
1975         {
1976           owterrain* NewPlace = protocontainer<owterrain>::GetProto(game::GetWorldMap()->GetWasPlaced()[j].X)->Spawn();
1977           v2 LocationPosition = game::GetWorldMap()->GetEntryPos(0, game::GetWorldMap()->GetWasPlaced()[j].Y);
1978           game::GetWorldMap()->GetWSquare(LocationPosition)->ChangeOWTerrain(NewPlace);
1979           game::GetWorldMap()->RevealEnvironment(LocationPosition, 1);
1980         }
1981 
1982         game::SaveWorldMap();
1983       }
1984 
1985       game::Save();
1986       game::Save(game::GetAutoSaveFileName());
1987     }
1988     else
1989       return false;
1990   }
1991   else
1992     ADD_MESSAGE("Got some scrolls of wishing.");
1993 
1994   for(int c = 0; c < 5; ++c)
1995     Char->GetStack()->AddItem(scrollofwishing::Spawn());
1996 
1997   return false;
1998 }
1999 
AutoPlay(character * Char)2000 truth commandsystem::AutoPlay(character* Char)
2001 {
2002   game::IncAutoPlayMode();
2003   return false;
2004 }
2005 
RaiseStats(character * Char)2006 truth commandsystem::RaiseStats(character* Char)
2007 {
2008   Char->EditAllAttributes(1);
2009   return false;
2010 }
2011 
LowerStats(character * Char)2012 truth commandsystem::LowerStats(character* Char)
2013 {
2014   Char->EditAllAttributes(-1);
2015   return false;
2016 }
2017 
GainAllItems(character * Char)2018 truth commandsystem::GainAllItems(character* Char)
2019 {
2020   itemvectorvector AllItems;
2021   protosystem::CreateEveryItem(AllItems);
2022   stack* Stack = game::IsInWilderness() ? Char->GetStack() : Char->GetStackUnder();
2023 
2024   for(uint c = 0; c < AllItems.size(); ++c)
2025     Stack->AddItem(AllItems[c][0]);
2026 
2027   return false;
2028 }
2029 
SeeWholeMap(character *)2030 truth commandsystem::SeeWholeMap(character*)
2031 {
2032   game::SeeWholeMap();
2033   return false;
2034 }
2035 
WalkThroughWalls(character *)2036 truth commandsystem::WalkThroughWalls(character*)
2037 {
2038   game::GoThroughWalls();
2039   return false;
2040 }
2041 
RaiseGodRelations(character *)2042 truth commandsystem::RaiseGodRelations(character*)
2043 {
2044   for(int c = 1; c <= GODS; ++c)
2045     game::GetGod(c)->AdjustRelation(50);
2046 
2047   return false;
2048 }
2049 
LowerGodRelations(character *)2050 truth commandsystem::LowerGodRelations(character*)
2051 {
2052   for(int c = 1; c <= GODS; ++c)
2053     game::GetGod(c)->AdjustRelation(-50);
2054 
2055   return false;
2056 }
2057 
GainDivineKnowledge(character *)2058 truth commandsystem::GainDivineKnowledge(character*)
2059 {
2060   for(int c = 1; c <= GODS; ++c)
2061     game::LearnAbout(game::GetGod(c));
2062 
2063   return false;
2064 }
2065 
SecretKnowledge(character * Char)2066 truth commandsystem::SecretKnowledge(character* Char)
2067 {
2068   felist List(CONST_S("Knowledge of the ancients"));
2069   List.AddEntry(CONST_S("Character attributes"), LIGHT_GRAY);
2070   List.AddEntry(CONST_S("Character attack info"), LIGHT_GRAY);
2071   List.AddEntry(CONST_S("Character defence info"), LIGHT_GRAY);
2072   List.AddEntry(CONST_S("Character danger values"), LIGHT_GRAY);
2073   List.AddEntry(CONST_S("Item attack info"), LIGHT_GRAY);
2074   List.AddEntry(CONST_S("Miscellaneous item info"), LIGHT_GRAY);
2075   List.AddEntry(CONST_S("Material info"), LIGHT_GRAY);
2076   game::SetStandardListAttributes(List);
2077   List.AddFlags(SELECTABLE);
2078   uint c, Chosen = List.Draw();
2079   festring Entry;
2080 
2081   if(Chosen & FELIST_ERROR_BIT)
2082     return false;
2083 
2084   List.Empty();
2085   List.RemoveFlags(SELECTABLE);
2086 
2087   if(Chosen < 4)
2088   {
2089     charactervector& Character = game::GetCharacterDrawVector();
2090     int TeamSize = 0;
2091 
2092     for(character* p : Char->GetTeam()->GetMember())
2093       if(p->IsEnabled())
2094       {
2095         Character.push_back(p);
2096         ++TeamSize;
2097       }
2098 
2099     protosystem::CreateEveryCharacter(Character);
2100     List.SetEntryDrawer(game::CharacterEntryDrawer);
2101 
2102     switch(Chosen)
2103     {
2104      case 0:
2105       List.AddDescription(CONST_S("                                             AS LS DX AG EN PE IN WS WL CH MA"));
2106 
2107       for(c = 0; c < Character.size(); ++c)
2108       {
2109         Entry.Empty();
2110         Character[c]->AddName(Entry, UNARTICLED);
2111         Character[c]->AddAttributeInfo(Entry);
2112         List.AddEntry(Entry, LIGHT_GRAY, 0, c);
2113       }
2114 
2115       List.SetPageLength(15);
2116       break;
2117      case 1:
2118       List.AddDescription(CONST_S("                                                  BD        THV       APC"));
2119 
2120       for(c = 0; c < Character.size(); ++c)
2121       {
2122         Entry.Empty();
2123         Character[c]->AddName(Entry, UNARTICLED);
2124         List.AddEntry(Entry, LIGHT_GRAY, 0, c);
2125         Character[c]->AddAttackInfo(List);
2126       }
2127 
2128       List.SetPageLength(20);
2129       break;
2130      case 2:
2131       List.AddDescription(CONST_S("                                                  DV/BV     HP        AV/BS"));
2132 
2133       for(c = 0; c < Character.size(); ++c)
2134       {
2135         Entry.Empty();
2136         Character[c]->AddName(Entry, UNARTICLED);
2137         Entry.Resize(47);
2138         Entry << int(Character[c]->GetDodgeValue());
2139         Entry.Resize(57);
2140         Entry << Character[c]->GetMaxHP();
2141         List.AddEntry(Entry, LIGHT_GRAY, 0, c);
2142         Character[c]->AddDefenceInfo(List);
2143       }
2144 
2145       List.SetPageLength(25);
2146       break;
2147      case 3:
2148       List.AddDescription(CONST_S("                                                  Danger    NGM       EGM"));
2149 
2150       for(c = 0; c < Character.size(); ++c)
2151       {
2152         Entry.Empty();
2153         Character[c]->AddName(Entry, UNARTICLED);
2154         Entry.Resize(47);
2155         Entry << int(Character[c]->GetRelativeDanger(Char, true) * 1000);
2156         Entry.Resize(57);
2157         const dangerid& DI = game::GetDangerMap().find(configid(Character[c]->GetType(),
2158                                                                 Character[c]->GetConfig()))->second;
2159         Entry << int(DI.NakedDanger * 1000);
2160         Entry.Resize(67);
2161         Entry << int(DI.EquippedDanger * 1000);
2162         List.AddEntry(Entry, LIGHT_GRAY, 0, c);
2163       }
2164 
2165       List.SetPageLength(15);
2166       break;
2167     }
2168 
2169     List.Draw();
2170 
2171     for(c = TeamSize; c < Character.size(); ++c)
2172       delete Character[c];
2173 
2174     game::ClearCharacterDrawVector();
2175   }
2176   else if(Chosen < 6)
2177   {
2178     itemvectorvector& Item = game::GetItemDrawVector();
2179     protosystem::CreateEveryItem(Item);
2180     List.SetEntryDrawer(game::ItemEntryDrawer);
2181     List.SetPageLength(20);
2182 
2183     switch(Chosen)
2184     {
2185      case 4:
2186       List.AddDescription(CONST_S("                                        Weight    Size      SR        DAM"));
2187 
2188       for(c = 0; c < Item.size(); ++c)
2189       {
2190         Entry.Empty();
2191         Item[c][0]->AddName(Entry, UNARTICLED);
2192         List.AddEntry(Entry, LIGHT_GRAY, 0, c, true);
2193         Item[c][0]->AddAttackInfo(List);
2194       }
2195 
2196       break;
2197      case 5:
2198       List.AddDescription(CONST_S("                                        \177              OV             NV"));
2199 
2200       for(c = 0; c < Item.size(); ++c)
2201       {
2202         Entry.Empty();
2203         Item[c][0]->AddName(Entry, UNARTICLED);
2204         List.AddEntry(Entry, LIGHT_GRAY, 0, c, true);
2205         Item[c][0]->AddMiscellaneousInfo(List);
2206       }
2207 
2208       break;
2209     }
2210 
2211     List.Draw();
2212 
2213     for(c = 0; c < Item.size(); ++c)
2214       delete Item[c][0];
2215 
2216     game::ClearItemDrawVector();
2217   }
2218   else if(Chosen < 7)
2219   {
2220     std::vector<material*> Material;
2221     protosystem::CreateEveryMaterial(Material);
2222     List.SetPageLength(30);
2223     List.AddDescription(CONST_S("                                        Str.  Flex. Dens. Int.  God"));
2224 
2225     for(c = 0; c < Material.size(); ++c)
2226     {
2227       Entry.Empty();
2228       Material[c]->AddName(Entry, false, false);
2229       Entry.Resize(40);
2230       Entry << Material[c]->GetStrengthValue();
2231       Entry.Resize(46);
2232       Entry << Material[c]->GetFlexibility();
2233       Entry.Resize(52);
2234       Entry << Material[c]->GetDensity();
2235       Entry.Resize(58);
2236       Entry << Material[c]->GetIntelligenceRequirement();
2237       Entry.Resize(64);
2238       Entry << game::GetGod(Material[c]->GetAttachedGod())->GetName();
2239       List.AddEntry(Entry, Material[c]->GetColor());
2240     }
2241 
2242     List.Draw();
2243 
2244     for(c = 0; c < Material.size(); ++c)
2245       delete Material[c];
2246   }
2247 
2248   List.PrintToFile(GetUserDataDir() + "secret" + Chosen + ".txt");
2249   ADD_MESSAGE("Info written also to %ssecret%d.txt.", GetUserDataDir().CStr(), Chosen);
2250   return false;
2251 }
2252 
DetachBodyPart(character * Char)2253 truth commandsystem::DetachBodyPart(character* Char)
2254 {
2255   Char->DetachBodyPart();
2256   return false;
2257 }
2258 
SetFireToBodyPart(character * Char)2259 truth commandsystem::SetFireToBodyPart(character* Char)
2260 {
2261   Char->SetFireToBodyPart();
2262   return false;
2263 }
2264 
SummonMonster(character * Char)2265 truth commandsystem::SummonMonster(character* Char)
2266 {
2267   character* Summoned = 0;
2268 
2269   while(!Summoned)
2270   {
2271     festring Temp;
2272 
2273     if(game::DefaultQuestion(Temp, CONST_S("Summon which monster?"), game::GetDefaultSummonMonster(), true) == ABORTED)
2274       return false;
2275 
2276     Summoned = protosystem::CreateMonster(Temp);
2277   }
2278 
2279   Summoned->SetTeam(game::GetTeam(MONSTER_TEAM));
2280   Summoned->PutNear(Char->GetPos());
2281   return false;
2282 }
2283 
LevelTeleport(character *)2284 truth commandsystem::LevelTeleport(character*)
2285 {
2286   long Level = game::NumberQuestion(CONST_S("To which level?"), WHITE);
2287 
2288   if(Level <= 0 || Level > game::GetLevels())
2289   {
2290     ADD_MESSAGE("There is no level %ld in this dungeon, %s!", Level, game::Insult());
2291     return false;
2292   }
2293 
2294   if(Level == game::GetCurrentLevelIndex() + 1)
2295   {
2296     ADD_MESSAGE("You are already here, %s!", game::Insult());
2297     return false;
2298   }
2299 
2300   return game::TryTravel(game::GetCurrentDungeonIndex(), Level - 1, RANDOM, true);
2301 }
2302 
Possess(character * Char)2303 truth commandsystem::Possess(character* Char)
2304 {
2305   int Dir = game::DirectionQuestion(CONST_S("Choose creature to possess. [press a direction key]"), false);
2306 
2307   if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir)))
2308     return false;
2309 
2310   character* ToPossess = Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir))->GetCharacter();
2311 
2312   if(ToPossess)
2313   {
2314     Char->RemoveFlags(C_PLAYER);
2315     game::SetPlayer(ToPossess);
2316   }
2317   else
2318     ADD_MESSAGE("There's no one to possess, %s!", game::Insult());
2319 
2320   return true; // The old player's turn must end
2321 }
2322 
Polymorph(character * Char)2323 truth commandsystem::Polymorph(character* Char)
2324 {
2325   character* NewForm;
2326 
2327   if(!Char->GetNewFormForPolymorphWithControl(NewForm)){
2328     return true;
2329   }
2330 
2331   Char->Polymorph(NewForm, game::NumberQuestion(CONST_S("For how long?"), WHITE));
2332   return true;
2333 }
2334 
2335 #endif
2336 
ToggleRunning(character * Char)2337 truth commandsystem::ToggleRunning(character* Char)
2338 {
2339   if(game::PlayerIsRunning()
2340      && PLAYER->StateIsActivated(PANIC)
2341      && PLAYER->GetTirednessState() != FAINTING)
2342   {
2343     ADD_MESSAGE("You are too scared to move at a normal pace.");
2344     return false;
2345   }
2346 
2347   if(!Char->CanMove())
2348   {
2349     ADD_MESSAGE("Well, well, aren't we funny today, eh?");
2350     return false;
2351   }
2352 
2353   game::SetPlayerIsRunning(!game::PlayerIsRunning());
2354   return false;
2355 }
2356 
IssueCommand(character * Char)2357 truth commandsystem::IssueCommand(character* Char)
2358 {
2359   if(!Char->CheckTalk())
2360     return false;
2361 
2362   return game::CommandQuestion();
2363 }
2364 
PlayerDiedWeaponSkills()2365 void commandsystem::PlayerDiedWeaponSkills()
2366 {
2367   commandsystem::ShowWeaponSkills(PLAYER);
2368 }
2369