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