command_character.cpp (25672B)
1 #include "scraps/game/action_eval/command.hpp" // IWYU pragma: associated 2 3 #include "scraps/game/action_eval/action_helper.hpp" 4 #include "scraps/game/action_eval/enter_leave.hpp" 5 #include "scraps/game/action_eval/test_helper.hpp" 6 #include "scraps/game/character_state.hpp" 7 #include "scraps/game/file_state.hpp" 8 #include "scraps/game/room_state.hpp" 9 #include "scraps/game/text_replace.hpp" 10 #include "scraps/string_utils.hpp" 11 #include "scraps/uuid.hpp" 12 13 #include <libshit/except.hpp> 14 15 #include <boost/container/static_vector.hpp> 16 17 #include <cstdint> 18 #include <optional> 19 #include <string> 20 #include <utility> 21 #include <variant> 22 23 // IWYU pragma: no_include <type_traits> 24 // IWYU pragma: no_forward_declare Scraps::Game::IdKey 25 // IWYU pragma: no_forward_declare Scraps::Game::NameKey 26 // IWYU pragma: no_forward_declare Scraps::Game::UuidKey 27 28 namespace Scraps::Game::ActionEvalPrivate 29 { 30 TEST_SUITE_BEGIN("Scraps::Game::ActionEval"); 31 32 CommandRes CommandCharacterActionSet(CommandParam p) 33 { 34 auto c = p.gc->GetCharacterColl().Get<NameKey>( 35 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 36 return c ? CommandActionSetCommon(p, c->GetActions()) : CommandRes::OK; 37 } 38 39 TEST_CASE_FIXTURE(Fixture, "CommandCharacterActionSet") 40 { 41 dummy.game.GetCharacters()[1].GetActions()[0].SetActive(false); 42 43 SetCmd("nosuch", "invalid", "", ""); 44 CHECK(CommandCharacterActionSet(c) == CommandRes::OK); 45 46 auto act = st.GetCharacterColl().At(1).GetActions().GetColl().At(0); 47 SetCmd("chara_1", "action_0-Active", "", ""); 48 CHECK(CommandCharacterActionSet(c) == CommandRes::OK); 49 CHECK(act.GetActive() == true); 50 } 51 52 CommandRes CommandPlayerActionSet(CommandParam p) 53 { return CommandActionSetCommon(p, p.gc->GetPlayer().GetActions()); } 54 55 // --------------------------------------------------------------------------- 56 57 static CommandRes CharaPropSet(CommandParam p, bool js) 58 { 59 auto parts = SplitMax<3>(p.Replace(p.eval.tmp_str0, p.cmd.GetParam0()), ':'); 60 if (parts.size() != 2) return CommandRes::OK; 61 auto c = p.gc->GetCharacterColl().Get<NameKey>(parts[0]); 62 if (!c) return CommandRes::OK; 63 64 return CommandCustomPropertySetCommon(p, c->GetProperties(), parts[1], js); 65 } 66 67 CommandRes CommandCharacterCustomPropertySet(CommandParam p) 68 { return CharaPropSet(p, false); } 69 CommandRes CommandCharacterCustomPropertySetJs(CommandParam p) 70 { return CharaPropSet(p, true); } 71 72 TEST_CASE_FIXTURE(Fixture, "CommandCharacterCustomPropertySet") 73 { 74 auto cp = st.GetCharacterColl().At(0).GetProperties(); 75 SetCmd("invalid", "Equals", "foo", ""); 76 CHECK(CommandCharacterCustomPropertySet(c) == CommandRes::OK); 77 SetCmd("chara_0:key_0:shit", "Equals", "foo", ""); 78 CHECK(CommandCharacterCustomPropertySet(c) == CommandRes::OK); 79 CHECK(cp.Get("key_0")->second == "Value 0"); 80 81 SetCmd("chara_0:key_0", "Equals", "10/2", ""); 82 CHECK(CommandCharacterCustomPropertySet(c) == CommandRes::OK); 83 CHECK(cp.Get("key_0")->second == "10/2"); 84 CHECK(CommandCharacterCustomPropertySetJs(c) == CommandRes::OK); 85 CHECK(cp.Get("key_0")->second == "5"); 86 } 87 88 static CommandRes PlayerPropSet(CommandParam p, bool js) 89 { 90 auto name = p.Replace(p.eval.tmp_str0, p.cmd.GetParam0()); 91 return CommandCustomPropertySetCommon( 92 p, p.gc->GetPlayer().GetProperties(), name, js); 93 } 94 95 CommandRes CommandPlayerCustomPropertySet(CommandParam p) 96 { return PlayerPropSet(p, false); } 97 CommandRes CommandPlayerCustomPropertySetJs(CommandParam p) 98 { return PlayerPropSet(p, true); } 99 100 TEST_CASE_FIXTURE(Fixture, "CommandPlayerCustomPropertySet") 101 { 102 SetCmd("key_0", "Equals", "foo", ""); 103 CHECK(CommandPlayerCustomPropertySet(c) == CommandRes::OK); 104 CHECK(st.GetPlayer().GetProperties().Get("key_0")->second == "foo"); 105 106 dummy.game.GetCharacters()[0].GetProperties()[0].SetName( 107 dummy.GetString("foo:bar")); 108 Reinit(); 109 SetCmd("foo:bar", "Equals", "2*3", ""); 110 CHECK(CommandPlayerCustomPropertySet(c) == CommandRes::OK); 111 CHECK(st.GetPlayer().GetProperties().Get("foo:bar")->second == "2*3"); 112 CHECK(CommandPlayerCustomPropertySetJs(c) == CommandRes::OK); 113 CHECK(st.GetPlayer().GetProperties().Get("foo:bar")->second == "6"); 114 } 115 116 // --------------------------------------------------------------------------- 117 118 static CommandRes DescriptionSetCommon(CommandParam p, CharacterProxy c) 119 { 120 p.Replace(p.eval.tmp_str0, p.cmd.GetParam3()); 121 c.SetDescription(p.eval.tmp_str0); 122 return CommandRes::OK; 123 } 124 125 CommandRes CommandCharacterDescriptionSet(CommandParam p) 126 { 127 return DescriptionSetCommon(p, p.gc->GetCharacterColl().At<NameKey>( 128 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0()))); 129 } 130 131 TEST_CASE_FIXTURE(Fixture, "CommandCharacterDescriptionSet") 132 { 133 SetCmd("nosuch", "", "", "foo"); 134 CHECK_THROWS(CommandCharacterDescriptionSet(c)); 135 136 SetCmd("chara_0", "", "", "new desc"); 137 CHECK(CommandCharacterDescriptionSet(c) == CommandRes::OK); 138 CHECK(st.GetCharacterColl().At(0).GetDescription() == "new desc"); 139 } 140 141 CommandRes CommandPlayerDescriptionSet(CommandParam p) 142 { return DescriptionSetCommon(p, p.gc->GetPlayer()); } 143 144 // --------------------------------------------------------------------------- 145 146 CommandRes CommandCharacterDescriptionShow(CommandParam p) 147 { 148 auto c = p.gc->GetCharacterColl().At<NameKey>( 149 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 150 p.gc.Log(c.GetDescription()); 151 return CommandRes::OK; 152 } 153 154 TEST_CASE_FIXTURE(Fixture, "CommandCharacterDescriptionShow") 155 { 156 SetCmd("nosuch", "", "", ""); 157 CHECK_THROWS(CommandCharacterDescriptionShow(c)); 158 CHECK(log == ""); 159 160 SetCmd("chara_0", "", "", ""); 161 CHECK(CommandCharacterDescriptionShow(c) == CommandRes::OK); 162 CHECK(log == "This is character #0\n"); log.clear(); 163 } 164 165 CommandRes CommandPlayerDescriptionShow(CommandParam p) 166 { 167 p.eval.tmp_str0.assign(p.gc->GetPlayer().GetDescription()); 168 ReplaceText(p.eval.tmp_str0, *p.gc, 0); 169 p.gc.Log(p.eval.tmp_str0); 170 return CommandRes::OK; 171 } 172 173 // --------------------------------------------------------------------------- 174 175 static CommandRes GenderSetCommon( 176 CommandParam p, CharacterProxy c, Libshit::StringView gender) 177 { 178 auto g = RagsStr2Gender(p.Replace(p.eval.tmp_str0, gender)); 179 if (!g) 180 LIBSHIT_THROW(ActionEvalError, "Invalid gender string", 181 "String", p.eval.tmp_str0); 182 183 c.SetGender(*g); 184 return CommandRes::OK; 185 } 186 187 CommandRes CommandCharacterGenderSet(CommandParam p) 188 { 189 auto c = p.gc->GetCharacterColl().Get<NameKey>( 190 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 191 return c ? GenderSetCommon(p, *c, p.cmd.GetParam1()) : CommandRes::OK; 192 } 193 194 TEST_CASE_FIXTURE(Fixture, "CommandCharacterGenderSet") 195 { 196 using G = Format::Proto::Gender; 197 auto chara = st.GetCharacterColl().At(0); 198 SetCmd("nosuch", "Female", "", ""); 199 CHECK(CommandCharacterGenderSet(c) == CommandRes::OK); 200 CHECK(chara.GetGender() == G::MALE); 201 202 auto check_ok = [&](const char* str, G gender) 203 { 204 CAPTURE(str); CAPTURE(gender); 205 SetCmd("chara_0", str, "", ""); 206 CHECK(CommandCharacterGenderSet(c) == CommandRes::OK); 207 CHECK(chara.GetGender() == gender); 208 }; 209 check_ok("Female", G::FEMALE); 210 check_ok("Male", G::MALE); 211 check_ok("Other", G::OTHER); 212 check_ok(" Male ", G::MALE); 213 214 SetCmd("chara_0", "male", "", ""); 215 CHECK_THROWS(CommandCharacterGenderSet(c)); 216 } 217 218 CommandRes CommandPlayerGenderSet(CommandParam p) 219 { return GenderSetCommon(p, p.gc->GetPlayer(), p.cmd.GetParam0()); } 220 221 // --------------------------------------------------------------------------- 222 223 static CommandRes ImageSetCommon( 224 CommandParam p, CharacterProxy c, Libshit::StringView fname) 225 { 226 auto f = p.gc->GetFileColl().StateGet<NameKey>( 227 p.Replace(p.eval.tmp_str0, fname)); 228 229 c.SetImageId(f ? f->id : FileId{}); 230 return CommandRes::OK; 231 } 232 233 CommandRes CommandCharacterImageSet(CommandParam p) 234 { 235 auto c = p.gc->GetCharacterColl().Get<NameKey>( 236 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 237 return c ? ImageSetCommon(p, *c, p.cmd.GetParam1()) : CommandRes::OK; 238 } 239 240 TEST_CASE_FIXTURE(Fixture, "CommandCharacterImageSet") 241 { 242 SetCmd("nosuch", "nosuch", "", ""); 243 CHECK(CommandCharacterImageSet(c) == CommandRes::OK); 244 245 auto chara = st.GetCharacterColl().At(1); 246 CHECK(chara.GetImageId() != FileId{}); 247 SetCmd("chara_1", "nosuch", "", ""); 248 CHECK(CommandCharacterImageSet(c) == CommandRes::OK); 249 CHECK(chara.GetImageId() == FileId{}); 250 251 SetCmd("chara_1", "file_2", "", ""); 252 CHECK(CommandCharacterImageSet(c) == CommandRes::OK); 253 CHECK(chara.GetImageId() == st.GetFileColl().At(2).GetId()); 254 } 255 256 CommandRes CommandPlayerImageSet(CommandParam p) 257 { return ImageSetCommon(p, p.gc->GetPlayer(), p.cmd.GetParam0()); } 258 259 // --------------------------------------------------------------------------- 260 261 CommandRes CommandPlayerImageSetOverlay(CommandParam p) 262 { 263 auto f = p.gc->GetFileColl().StateGet<NameKey>( 264 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 265 266 p.gc->GetPlayer().SetOverlayImageId(f ? f->id : FileId{}); 267 return CommandRes::OK; 268 } 269 270 TEST_CASE_FIXTURE(Fixture, "CommandPlayerImageSetOverlay") 271 { 272 SetCmd("file_0", "", "", ""); 273 CHECK(CommandPlayerImageSetOverlay(c) == CommandRes::OK); 274 CHECK(st.GetPlayer().GetOverlayImageId() == st.GetFileColl().At(0).GetId()); 275 276 SetCmd("nosuch", "", "", ""); 277 CHECK(CommandPlayerImageSetOverlay(c) == CommandRes::OK); 278 CHECK(st.GetPlayer().GetOverlayImageId() == FileId{}); 279 } 280 281 // --------------------------------------------------------------------------- 282 283 CommandRes CommandCharacterImageShow(CommandParam p) 284 { 285 auto c = p.gc->GetCharacterColl().Get<NameKey>( 286 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 287 if (!c) return CommandRes::OK; 288 289 if (p.gc->GetInlineImages()) 290 { 291 auto f = p.gc->GetRawFileColl().StateAt<IdKey>(c->GetImageId()); 292 p.gc.ImageLog(f.id); 293 } 294 else if (p.gc->GetShowMainImage()) 295 { 296 auto f = p.gc->GetFileColl().Get<IdKey>(c->GetImageId()); 297 if (f && f->IsValidImage()) 298 { 299 p.gc->SetMainImageId(f->GetId()); 300 p.gc->SetMainOverlayImageId(f->GetId()); 301 } 302 } 303 304 return CommandRes::OK; 305 } 306 307 TEST_CASE_FIXTURE(Fixture, "CommandCharacterImageShow") 308 { 309 st.GetCharacterColl().At(1).SetImageId(FileId{1337}); 310 auto check = [&](const char* chara, FileId id) 311 { 312 st.SetMainImageId(FileId{1234}); 313 st.SetMainOverlayImageId(FileId{1234}); 314 SetCmd(chara, "", "", ""); 315 CHECK(CommandCharacterImageShow(c) == CommandRes::OK); 316 CHECK(st.GetMainImageId() == id); 317 CHECK(st.GetMainOverlayImageId() == id); 318 }; 319 check("nosuch", FileId{1234}); 320 check("chara_0", st.GetFileColl().At(0).GetId()); 321 check("chara_1", FileId{1234}); 322 323 dummy.game.SetShowMainImage(false); 324 check("chara_0", FileId{1234}); 325 check("chara_1", FileId{1234}); 326 327 dummy.game.SetInlineImages(true); 328 FileId fid{445566}; 329 gc.SetImageLogFunction([&](FileId i) { fid = i; }); 330 331 check("nosuch", FileId{1234}); 332 CHECK(fid == FileId{445566}); 333 334 fid = {}; 335 check("chara_0", FileId{1234}); 336 CHECK(fid == st.GetFileColl().At(0).GetId()); 337 338 fid = FileId{665544}; 339 SetCmd("chara_1", "", "", ""); 340 CHECK_THROWS(CommandCharacterImageShow(c)); 341 CHECK(fid == FileId{665544}); 342 } 343 344 // --------------------------------------------------------------------------- 345 346 namespace 347 { 348 struct CommandCharacterMoveCoro final : EvalItem 349 { 350 CommandCharacterMoveCoro( 351 CharacterProxy c, CommandCtx& ctx, Libshit::StringView param3) 352 : c{c}, ctx{ctx}, param3{param3} {} 353 ActionResult Call(ActionEvalState& eval, GameController& gc) override; 354 CharacterProxy c; 355 CommandCtx& ctx; 356 Libshit::StringView param3; 357 std::uint8_t state = 0; 358 }; 359 } 360 361 ActionResult CommandCharacterMoveCoro::Call( 362 ActionEvalState& eval, GameController& gc) 363 { 364 SCRAPS_AE_SWITCH() 365 { 366 ctx.cmd_res = CommandRes::OK; 367 if (auto p = gc->GetPlayer(); c.GetRoomId() == p.GetRoomId()) 368 { 369 auto a = p.GetActions().GetColl().Get<NameKey>( 370 "<<On Character Leave>>"); 371 if (a) SCRAPS_AE_CALL_DEFER(1, OuterAction, *a, ObjectId{}); 372 } 373 SCRAPS_AE_RESUME(1); 374 375 eval.tmp_str0.assign(param3); 376 ReplaceText(eval.tmp_str0, *gc, ctx.loop_item); 377 378 RoomId dest{}; 379 if (eval.tmp_str0 == Uuid::VOID_ROOM_STR); 380 else if (eval.tmp_str0 == Uuid::PLAYER_ROOM_STR) 381 dest = gc->GetPlayer().GetRoomId(); 382 else if (auto uuid = Uuid::TryParse(eval.tmp_str0)) 383 { 384 if (auto r = gc->GetRoomColl().StateGet<UuidKey>(*uuid)) 385 dest = r->id; 386 } 387 else 388 { 389 if (auto r = gc->GetRoomColl().StateGet<NameKey>(eval.tmp_str0)) 390 dest = r->id; 391 else 392 { 393 eval.query_title = "Error in command CT_MoveChar. Could not locate " 394 "a room called " + eval.tmp_str0; 395 SCRAPS_AE_GENERIC_RETURN(ActionResult::MSGBOX); 396 } 397 } 398 399 c.SetRoomId(dest); 400 401 auto p = gc->GetPlayer(); 402 if (dest == p.GetRoomId()) 403 { 404 auto a = p.GetActions().GetColl().Get<NameKey>( 405 "<<On Character Enter>>"); 406 if (a) SCRAPS_AE_TAIL_CALL(OuterAction, *a, ObjectId{}); 407 } 408 409 SCRAPS_AE_RETURN(); 410 } 411 } 412 413 CommandRes CommandCharacterMove(CommandParam p) 414 { 415 auto c = p.gc->GetCharacterColl().At<NameKey>( 416 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 417 p.eval.stack.Push<CommandCharacterMoveCoro>(c, p.ctx, p.cmd.GetParam1()); 418 return CommandRes::CALL; 419 } 420 421 TEST_CASE_FIXTURE(Fixture, "CommandCharacterMove") 422 { 423 SetCmd("nosuch", "nosuch", "", ""); 424 CHECK_THROWS(CommandCharacterMove(c)); 425 426 { 427 auto ch = st.GetCharacterColl().At(1); 428 // moving to a non-existing name room -> msgbox 429 auto orig = ch.GetRoomId(); 430 SetCmd("chara_1", "nosuch", "", ""); 431 REQUIRE(CommandCharacterMove(c) == CommandRes::CALL); 432 ctx.cmd_res = CommandRes::CALL; 433 CHECK(Call(eval, gc) == ActionResult::MSGBOX); 434 CHECK(eval.query_title == "Error in command CT_MoveChar. Could not " 435 "locate a room called nosuch"); 436 REQUIRE(eval.stack.Empty()); 437 CHECK(ch.GetRoomId() == orig); 438 CHECK(ctx.cmd_res == CommandRes::OK); 439 440 auto simple_move = [&](Libshit::NonowningString room, RoomId rid) 441 { 442 SetCmd("chara_1", room, "", ""); 443 REQUIRE(CommandCharacterMove(c) == CommandRes::CALL); 444 ctx.cmd_res = CommandRes::CALL; 445 CallCheckLast(); 446 CHECK(ch.GetRoomId() == rid); 447 CHECK(ctx.cmd_res == CommandRes::OK); 448 }; 449 // moving to a non-existing UUID room -> move to void 450 simple_move("12345678-1234-5678-9abc-123456789012", {}); 451 // moving to an existing room 452 simple_move("room_2", st.GetRoomColl().At(2).GetId()); 453 simple_move("00000000000040040000000000000001", 454 st.GetRoomColl().At(1).GetId()); 455 456 // special cases, no actions 457 simple_move(Uuid::PLAYER_ROOM_STR, st.GetPlayerRoom().GetId()); 458 simple_move(Uuid::VOID_ROOM_STR, {}); 459 } 460 461 // generate those actions 462 auto acts = dummy.game.GetCharacters()[0].GetActions(); 463 acts[0].SetName(dummy.GetString("<<On Character Enter>>")); 464 acts[1].SetName(dummy.GetString("<<On Character Leave>>")); 465 Reinit(); 466 467 auto ch = st.GetCharacterColl().At(1); 468 // move to player, with action 469 SetCmd("chara_1", "room_0", "", ""); 470 REQUIRE(CommandCharacterMove(c) == CommandRes::CALL); 471 ctx.cmd_res = CommandRes::CALL; 472 CHECK(Call(eval, gc) == ActionResult::IDLE); 473 CHECK(ch.GetRoomId() == st.GetRoomColl().At(0).GetId()); 474 CHECK(ctx.cmd_res == CommandRes::OK); 475 REQUIRE(eval.stack.Size() == 1); // tail call 476 CallCheckOa(st.GetPlayer().GetActions().GetColl().At(0).GetId(), {}); 477 478 // move from player, with action 479 auto check = [&](const char* room) 480 { 481 SetCmd("chara_1", room, "", ""); 482 REQUIRE(CommandCharacterMove(c) == CommandRes::CALL); 483 ctx.cmd_res = CommandRes::CALL; 484 CHECK(Call(eval, gc) == ActionResult::IDLE); 485 CHECK(ch.GetRoomId() == st.GetRoomColl().At(0).GetId()); // still in old room 486 REQUIRE(eval.stack.Size() == 2); // normal call 487 CallCheckOa(st.GetPlayer().GetActions().GetColl().At(1).GetId(), {}); 488 }; 489 check("room_1"); 490 CallCheckLast(); 491 CHECK(ch.GetRoomId() == st.GetRoomColl().At(1).GetId()); // new 492 CHECK(ctx.cmd_res == CommandRes::OK); 493 494 // in case of exception, chara not moved, but action still execd 495 ch.SetRoomId(st.GetRoomColl().At(0).GetId()); 496 check("nosuch"); 497 CHECK(Call(eval, gc) == ActionResult::MSGBOX); 498 REQUIRE(eval.stack.Empty()); 499 eval.stack.Clear(); 500 CHECK(ch.GetRoomId() == st.GetRoomColl().At(0).GetId()); 501 CHECK(ctx.cmd_res == CommandRes::OK); 502 } 503 504 CommandRes CommandPlayerMove(CommandParam p) 505 { 506 auto uuid = Uuid::TryParse(p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 507 auto rooms = p.gc->GetRoomColl(); 508 // this doesn't throw in rags, but instead moves player to the void room, 509 // and hell breaks loose 510 auto& r = uuid ? rooms.StateAt<UuidKey>(*uuid) : 511 rooms.StateAt<NameKey>(p.eval.tmp_str0); 512 p.gc->GetPlayer().SetRoomId(r.id); 513 514 PushClearCtx(p); 515 p.eval.stack.Push<RoomEntered>(false); 516 return CommandRes::CALL; 517 } 518 519 TEST_CASE_FIXTURE(Fixture, "CommandPlayerMove") 520 { 521 SetCmd("nosuch", "", "", ""); 522 CHECK_THROWS(CommandPlayerMove(c)); 523 SetCmd("ffffffffffffffffffffffffffffffff", "", "", ""); 524 CHECK_THROWS(CommandPlayerMove(c)); 525 526 SetCmd("room_1", "", "", ""); 527 CHECK(CommandPlayerMove(c) == CommandRes::CALL); 528 ctx.cmd_res = CommandRes::CALL; 529 auto rid = st.GetRoomColl().At(1).GetId(); 530 CHECK(st.GetPlayer().GetRoomId() == rid); 531 532 REQUIRE(eval.stack.Size() == 2); 533 CHECK(dynamic_cast<RoomEntered*>(&eval.stack.Back())); 534 eval.stack.Pop(); 535 CallCheckLast(); 536 CHECK(ctx.cmd_res == CommandRes::OK); 537 } 538 539 // --------------------------------------------------------------------------- 540 541 CommandRes CommandCharacterMoveToObject(CommandParam p) 542 { 543 auto c = p.gc->GetCharacterColl().Get<NameKey>( 544 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 545 if (!c) return CommandRes::OK; 546 auto o = GetObjectUuid(*p.gc, p.Replace(p.eval.tmp_str0, p.cmd.GetParam1())); 547 if (!o) return CommandRes::OK; 548 549 if (auto l = o->GetLocation(); auto rid = std::get_if<RoomId>(&l)) 550 { 551 c->SetRoomId(*rid); 552 if (*rid == p.gc->GetPlayer().GetRoomId()) 553 RoomEnteredSimple(p.eval, p.gc); 554 } 555 return CommandRes::OK; 556 } 557 558 TEST_CASE_FIXTURE(Fixture, "CommandCharacterMoveToObject") 559 { 560 SetCmd("nosuch", "nosuch", "", ""); 561 CHECK(CommandCharacterMoveToObject(c) == CommandRes::OK); 562 SetCmd("nosuch", "00000000000b1ec10000000000000000", "", ""); 563 CHECK(CommandCharacterMoveToObject(c) == CommandRes::OK); 564 565 auto chara = p.gc->GetCharacterColl().At(1); 566 auto old = chara.GetRoomId(); 567 SetCmd("chara_1", "nosuch", "", ""); 568 CHECK(CommandCharacterMoveToObject(c) == CommandRes::OK); 569 CHECK(chara.GetRoomId() == old); 570 571 // object 0 is nowhere 572 auto obj = p.gc->GetObjectColl().At(0); 573 REQUIRE(std::holds_alternative<LocationNone>(obj.GetLocation())); 574 SetCmd("chara_1", "00000000000b1ec10000000000000000", "", ""); 575 CHECK(CommandCharacterMoveToObject(c) == CommandRes::OK); 576 CHECK(chara.GetRoomId() == old); 577 578 // obj 0 is in a room 579 obj.SetLocation(p.gc->GetRoomColl().At(2).GetId()); 580 SetCmd("chara_1", "00000000000b1ec10000000000000000", "", ""); 581 CHECK(CommandCharacterMoveToObject(c) == CommandRes::OK); 582 CHECK(chara.GetRoomId() == p.gc->GetRoomColl().At(2).GetId()); 583 CHECK(log == ""); 584 585 // obj0 is next to the player 586 obj.SetLocation(p.gc->GetPlayer().GetRoomId()); 587 CHECK(CommandCharacterMoveToObject(c) == CommandRes::OK); 588 CHECK(chara.GetRoomId() == p.gc->GetPlayer().GetRoomId()); 589 CHECK(log == "\nThis is room #0\nCharacter #1 is here.\n"); 590 log.clear(); 591 } 592 593 // --------------------------------------------------------------------------- 594 595 CommandRes CommandPlayerMoveToCharacter(CommandParam p) 596 { 597 auto c = p.gc->GetCharacterColl().Get<NameKey>( 598 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 599 auto pl = p.gc->GetPlayer(); 600 if (!c || c->GetRoomId() == RoomId{} || c->GetRoomId() == pl.GetRoomId()) 601 return CommandRes::OK; 602 603 pl.SetRoomId(c->GetRoomId()); 604 PushClearCtx(p); 605 p.eval.stack.Push<RoomEntered>(false); 606 return CommandRes::CALL; 607 } 608 609 TEST_CASE_FIXTURE(Fixture, "CommandPlayerMoveToCharacter") 610 { 611 auto rid = st.GetPlayer().GetRoomId(); 612 SetCmd("nosuch", "", "", ""); 613 CHECK(CommandPlayerMoveToCharacter(c) == CommandRes::OK); 614 CHECK(st.GetPlayer().GetRoomId() == rid); 615 616 // nowhere -> not moved 617 st.GetCharacterColl().At(1).SetRoomId({}); 618 SetCmd("chara_1", "", "", ""); 619 CHECK(CommandPlayerMoveToCharacter(c) == CommandRes::OK); 620 CHECK(st.GetPlayer().GetRoomId() == rid); 621 622 // same room as player -> not moved 623 st.GetCharacterColl().At(1).SetRoomId(rid); 624 CHECK(CommandPlayerMoveToCharacter(c) == CommandRes::OK); 625 CHECK(st.GetPlayer().GetRoomId() == rid); 626 627 // different room -> moved 628 st.GetCharacterColl().At(1).SetRoomId(st.GetRoomColl().At(1).GetId()); 629 CHECK(CommandPlayerMoveToCharacter(c) == CommandRes::CALL); 630 ctx.cmd_res = CommandRes::CALL; 631 REQUIRE(eval.stack.Size() == 2); 632 CHECK(dynamic_cast<RoomEntered*>(&eval.stack.Back())); 633 CHECK(st.GetPlayer().GetRoomId() == st.GetRoomColl().At(1).GetId()); 634 eval.stack.Pop(); 635 CallCheckLast(); 636 CHECK(ctx.cmd_res == CommandRes::OK); 637 } 638 639 // --------------------------------------------------------------------------- 640 641 CommandRes CommandPlayerMoveToObject(CommandParam p) 642 { 643 auto o = GetObjectUuid(*p.gc, p.Replace(p.eval.tmp_str0, p.cmd.GetParam0())); 644 if (!o || !std::holds_alternative<RoomId>(o->GetLocation())) 645 return CommandRes::OK; 646 647 auto pl = p.gc->GetPlayer(); 648 pl.SetRoomId(std::get<RoomId>(o->GetLocation())); 649 PushClearCtx(p); 650 p.eval.stack.Push<RoomEntered>(false); 651 return CommandRes::CALL; 652 } 653 654 TEST_CASE_FIXTURE(Fixture, "CommandPlayerMoveToObject") 655 { 656 auto rid = st.GetPlayer().GetRoomId(); 657 SetCmd("invalid", "", "", ""); 658 CHECK(CommandPlayerMoveToObject(c) == CommandRes::OK); 659 CHECK(st.GetPlayer().GetRoomId() == rid); 660 SetCmd("ffffffffffffffffffffffffffffffff", "", "", ""); 661 CHECK(CommandPlayerMoveToObject(c) == CommandRes::OK); 662 CHECK(st.GetPlayer().GetRoomId() == rid); 663 664 // not in a room -> not moved 665 st.GetObjectColl().At(0).SetLocation(st.GetObjectColl().At(0).GetId()); 666 SetCmd("00000000000b1ec10000000000000000", "", "", ""); 667 CHECK(CommandPlayerMoveToObject(c) == CommandRes::OK); 668 CHECK(st.GetPlayer().GetRoomId() == rid); 669 670 // in a room room -> "moved" 671 st.GetObjectColl().At(0).SetLocation(rid); 672 CHECK(CommandPlayerMoveToObject(c) == CommandRes::CALL); 673 ctx.cmd_res = CommandRes::CALL; 674 REQUIRE(eval.stack.Size() == 2); 675 CHECK(dynamic_cast<RoomEntered*>(&eval.stack.Back())); 676 CHECK(st.GetPlayer().GetRoomId() == rid); 677 eval.stack.Pop(); 678 CallCheckLast(); 679 CHECK(ctx.cmd_res == CommandRes::OK); 680 681 // in a different room 682 st.GetObjectColl().At(0).SetLocation(st.GetRoomColl().At(1).GetId()); 683 CHECK(CommandPlayerMoveToObject(c) == CommandRes::CALL); 684 ctx.cmd_res = CommandRes::CALL; 685 REQUIRE(eval.stack.Size() == 2); 686 CHECK(dynamic_cast<RoomEntered*>(&eval.stack.Back())); 687 CHECK(st.GetPlayer().GetRoomId() == st.GetRoomColl().At(1).GetId()); 688 eval.stack.Pop(); 689 CallCheckLast(); 690 CHECK(ctx.cmd_res == CommandRes::OK); 691 } 692 693 // --------------------------------------------------------------------------- 694 695 CommandRes CommandCharacterNameOverrideSet(CommandParam p) 696 { 697 if (auto c = p.gc->GetCharacterColl().Get<NameKey>( 698 p.Replace(p.eval.tmp_str0, p.cmd.GetParam0()))) 699 { 700 auto val = p.Replace(p.eval.tmp_str0, p.cmd.GetParam2()); 701 c->SetNameOverride(std::string{val}); 702 } 703 704 return CommandRes::OK; 705 } 706 707 TEST_CASE_FIXTURE(Fixture, "CommandCharacterNameOverrideSet") 708 { 709 auto chara = st.GetCharacterColl().At(0); 710 SetCmd("nosuch", "", "foo", ""); 711 CHECK(CommandCharacterNameOverrideSet(c) == CommandRes::OK); 712 CHECK(chara.GetNameOverride() == "Character #0"); 713 714 SetCmd("chara_0", "", "bar", ""); 715 CHECK(CommandCharacterNameOverrideSet(c) == CommandRes::OK); 716 CHECK(chara.GetNameOverride() == "bar"); 717 718 chara.SetNameOverride("<[playername]>"); 719 SetCmd("chara_0", "", "{[playername]}", ""); 720 CHECK(CommandCharacterNameOverrideSet(c) == CommandRes::OK); 721 CHECK(chara.GetNameOverride() == "{<[playername]>}"); 722 } 723 724 CommandRes CommandPlayerNameOverrideSet(CommandParam p) 725 { 726 auto val = p.ReplaceDouble(p.eval.tmp_str0, p.cmd.GetParam2()); 727 p.gc->GetPlayer().SetNameOverride(std::string{val}); 728 729 return CommandRes::OK; 730 } 731 732 TEST_CASE_FIXTURE(Fixture, "CommandPlayerNameOverrideSet") 733 { 734 auto pl = st.GetCharacterColl().At(0); 735 SetCmd("", "", "bar", ""); 736 CHECK(CommandPlayerNameOverrideSet(c) == CommandRes::OK); 737 CHECK(pl.GetNameOverride() == "bar"); 738 739 pl.SetNameOverride("<[playername]>"); 740 SetCmd("", "", "{[playername]}", ""); 741 CHECK(CommandPlayerNameOverrideSet(c) == CommandRes::OK); 742 CHECK(pl.GetNameOverride() == "{<<[playername]>>}"); 743 } 744 745 TEST_SUITE_END(); 746 }