if_object.cpp (14308B)
1 #include "scraps/game/action_eval/if.hpp" // IWYU pragma: associated 2 3 #include "scraps/algorithm.hpp" 4 #include "scraps/game/action_eval/action_helper.hpp" 5 #include "scraps/game/action_eval/test_helper.hpp" 6 #include "scraps/game/character_state.hpp" 7 #include "scraps/game/room_state.hpp" 8 #include "scraps/uuid.hpp" 9 10 #include <libshit/nonowning_string.hpp> 11 12 #include <functional> 13 #include <iterator> 14 #include <variant> 15 16 // IWYU pragma: no_include <type_traits> 17 // IWYU pragma: no_forward_declare Scraps::Game::IdKey 18 // IWYU pragma: no_forward_declare Scraps::Game::NameKey 19 // IWYU pragma: no_forward_declare Scraps::Game::UuidKey 20 21 namespace Scraps::Game::ActionEvalPrivate 22 { 23 TEST_SUITE_BEGIN("Scraps::Game::ActionEval"); 24 25 std::optional<bool> IfObjectCustomProperty(IfParam p) 26 { 27 RAGS_IF_CUSTOM_PROPERTY_GET_PARTS; 28 auto o = GetObjectSelfOrName(*p.gc, parts[0], p.oa.object); 29 if (!o) return {}; 30 return IfCustomPropertyCommon(p, o->GetProperties(), parts[1]); 31 } 32 33 TEST_CASE_FIXTURE(Fixture, "IfObjectCustomProperty") 34 { 35 SetCk("object_0:key_0", "Equals", "Value 0"); 36 CHECK(IfObjectCustomProperty(p) == true); 37 38 SetCk("<Self>:key_0", "Equals", "Value 0"); 39 CHECK(IfObjectCustomProperty(p) == std::nullopt); 40 p.oa.object = st.GetObjectColl().StateAt(0).id; 41 CHECK(IfObjectCustomProperty(p) == true); 42 43 SetCk("<self>:key_0", "Equals", "Value 0"); 44 CHECK(IfObjectCustomProperty(p) == std::nullopt); // case sensitive 45 } 46 47 // --------------------------------------------------------------------------- 48 49 static std::optional<ObjectProxy> GetObject( 50 IfParam p, Libshit::StringView name) 51 { 52 name = p.Replace(p.eval.tmp_str0, name); 53 return GetObjectUuidOrName(*p.gc, name); 54 } 55 56 std::optional<bool> IfObjectInCharacter(IfParam p) 57 { 58 auto o = GetObject(p, p.check.GetParam1()); if (!o) return false; 59 auto loc = o->GetLocation(); 60 auto cid = std::get_if<CharacterId>(&loc); if (!cid) return false; 61 62 auto name = p.Replace(p.eval.tmp_str0, p.check.GetParam0()); 63 auto c = p.gc->GetRawCharacterColl().StateGet<NameKey>(name); 64 if (!c || name != c->name) return false; // case sensitive name check 65 66 return *cid == c->id; 67 } 68 69 TEST_CASE_FIXTURE(Fixture, "IfObjectInCharacter") 70 { 71 SetCk("nosuch", "nosuch", ""); 72 CHECK(IfObjectInCharacter(p) == false); 73 74 SetCk("nosuch", "object_0", ""); 75 CHECK(IfObjectInCharacter(p) == false); 76 SetCk("chara_0", "object_0", ""); 77 CHECK(IfObjectInCharacter(p) == false); 78 st.GetObjectColl().At(0).SetLocation(st.GetCharacterColl().At(0).GetId()); 79 CHECK(IfObjectInCharacter(p) == true); 80 81 st.GetObjectColl().At(1).SetLocation(st.GetObjectColl().At(0).GetId()); 82 SetCk("chara_0", "object_1", ""); 83 CHECK(IfObjectInCharacter(p) == false); // not recursive 84 85 SetCk("chara_0", "Object_0", ""); 86 CHECK(IfObjectInCharacter(p) == true); 87 SetCk("chara_0", "00000000-000b-1ec1-0000-000000000000", ""); 88 CHECK(IfObjectInCharacter(p) == true); 89 SetCk("Chara_0", "00000000-000b-1ec1-0000-000000000000", ""); 90 CHECK(IfObjectInCharacter(p) == false); // not case sensitive 91 } 92 93 // --------------------------------------------------------------------------- 94 95 std::optional<bool> IfObjectInGroup(IfParam p) 96 { 97 auto name = p.Replace(p.eval.tmp_str0, p.check.GetParam0()); 98 auto o = p.gc->GetObjectColl().Get<NameKey>(name); 99 100 return IfInGroupCommon( 101 p, p.gc->GetObjectGroupColl(), o ? o->GetGroupId() : GroupId{0}, 102 p.check.GetParam1(), ""); 103 } 104 105 TEST_CASE_FIXTURE(Fixture, "IfObjectInGroup") 106 { 107 SetCk("nosuch", "", ""); 108 CHECK(IfObjectInGroup(p) == true); 109 SetCk("nosuch", "object_group_0", ""); 110 CHECK(IfObjectInGroup(p) == false); 111 112 SetCk("object_0", "object_group_0", ""); 113 CHECK(IfObjectInGroup(p) == true); // direct 114 SetCk("Object_0", "object_group_0", ""); 115 CHECK(IfObjectInGroup(p) == true); // direct, case insensitive 116 SetCk("object_2", "object_group_0", ""); 117 CHECK(IfObjectInGroup(p) == true); // in-direct 118 SetCk("object_2", "object_group_2", ""); 119 CHECK(IfObjectInGroup(p) == true); // direct 120 SetCk("object_2", "Object_group_2", ""); 121 CHECK(IfObjectInGroup(p) == false); // case sensitive 122 } 123 124 // --------------------------------------------------------------------------- 125 126 // why is "not" a fucking keyword in c++ 127 static bool IfObjectInObjectCommon(IfParam p, bool not_) 128 { 129 auto o = GetObject(p, p.check.GetParam0()); if (!o) return false; 130 auto loc = o->GetLocation(); 131 auto oid = std::get_if<ObjectId>(&loc); if (!oid) return not_; 132 133 // TODO: this should be a case sensitive match 134 auto oo = StateGetObjectUuid( 135 *p.gc, p.Replace(p.eval.tmp_str0, p.check.GetParam1())); 136 if (!oo) return not_; 137 138 return (*oid == oo->id) ^ not_; 139 } 140 141 std::optional<bool> IfObjectInObject(IfParam p) 142 { return IfObjectInObjectCommon(p, false); } 143 144 TEST_CASE_FIXTURE(Fixture, "IfObjectInObject") 145 { 146 SetCk("nosuch", "nosuch", ""); 147 CHECK(IfObjectInObject(p) == false); 148 149 SetCk("object_0", "nosuch", ""); 150 CHECK(IfObjectInObject(p) == false); 151 SetCk("object_0", "00000000-000b-1ec1-0000-000000000001", ""); 152 CHECK(IfObjectInObject(p) == false); 153 st.GetObjectColl().At(0).SetLocation(st.GetObjectColl().At(1).GetId()); 154 CHECK(IfObjectInObject(p) == true); 155 // TODO case sensitive 156 // Set("object_0", "00000000-000b-1EC1-0000-000000000001", ""); 157 // CHECK(IfObjectInObject(p) == false); 158 159 st.GetObjectColl().At(1).SetLocation(st.GetObjectColl().At(2).GetId()); 160 SetCk("object_0", "00000000-000b-1ec1-0000-000000000002", ""); 161 CHECK(IfObjectInObject(p) == false); // not recursive 162 } 163 164 // --------------------------------------------------------------------------- 165 166 std::optional<bool> IfObjectInPlayer(IfParam p) 167 { 168 auto o = GetObject(p, p.check.GetParam0()); if (!o) return false; 169 auto loc = o->GetLocation(); 170 171 while (auto oid = std::get_if<ObjectId>(&loc)) 172 { 173 o = p.gc->GetObjectColl().Get<IdKey>(*oid); if (!o) return false; 174 loc = o->GetLocation(); 175 } 176 177 auto cid = std::get_if<CharacterId>(&loc); if (!cid) return false; 178 return *cid == p.gc->GetPlayerId(); 179 } 180 181 TEST_CASE_FIXTURE(Fixture, "IfObjectInPlayer") 182 { 183 SetCk("nosuch", "", ""); 184 CHECK(IfObjectInPlayer(p) == false); 185 SetCk("Object_0", "", ""); 186 CHECK(IfObjectInPlayer(p) == false); 187 st.GetObjectColl().At(0).SetLocation(st.GetPlayerId()); 188 CHECK(IfObjectInPlayer(p) == true); 189 190 st.GetObjectColl().At(1).SetLocation(st.GetObjectColl().At(0).GetId()); 191 SetCk("object_1", "", ""); 192 CHECK(IfObjectInPlayer(p) == true); // recursive... 193 } 194 195 // --------------------------------------------------------------------------- 196 197 std::optional<bool> IfObjectInRoom(IfParam p) 198 { 199 auto o = GetObject(p, p.check.GetParam0()); if (!o) return false; 200 auto loc = o->GetLocation(); 201 auto rid = std::get_if<RoomId>(&loc); if (!rid) return false; 202 203 auto room = p.Replace(p.eval.tmp_str0, p.check.GetParam1()); 204 if (room == Uuid::PLAYER_ROOM_STR) 205 return *rid == p.gc->GetPlayer().GetRoomId(); 206 207 auto uuid = Uuid::TryParse(room); 208 if (!uuid) return false; 209 auto oo = p.gc->GetRawRoomColl().StateGet<UuidKey>(*uuid); 210 // TODO: this should be a case sensitive match 211 if (!oo) return false; 212 return *rid == oo->id; 213 } 214 215 TEST_CASE_FIXTURE(Fixture, "IfObjectInRoom") 216 { 217 SetCk("nosuch", "nosuch", ""); 218 CHECK(IfObjectInRoom(p) == false); 219 220 SetCk("object_0", "nosuch", ""); 221 CHECK(IfObjectInRoom(p) == false); 222 SetCk("object_0", "00000000-0000-4004-0000-000000000000", ""); 223 CHECK(IfObjectInRoom(p) == false); 224 st.GetObjectColl().At(0).SetLocation(st.GetRoomColl().At(0).GetId()); 225 CHECK(IfObjectInRoom(p) == true); 226 227 SetCk("object_0", Uuid::PLAYER_ROOM_STR, ""); 228 CHECK(IfObjectInRoom(p) == true); 229 st.GetPlayer().SetRoomId(st.GetRoomColl().At(1).GetId()); 230 CHECK(IfObjectInRoom(p) == false); 231 232 // void room - will always be false 233 SetCk("object_0", Uuid::PLAYER_ROOM_STR, ""); 234 CHECK(IfObjectInRoom(p) == false); 235 st.GetObjectColl().At(0).SetLocation(LocationNone{}); 236 CHECK(IfObjectInRoom(p) == false); 237 } 238 239 // --------------------------------------------------------------------------- 240 241 std::optional<bool> IfObjectInRoomGroup(IfParam p) 242 { 243 // name not allowed, only uuid so can't use GetObject 244 auto o = GetObjectUuid( 245 *p.gc, p.Replace(p.eval.tmp_str0, p.check.GetParam0())); 246 if (!o) return false; 247 248 auto loc = o->GetLocation(); 249 auto rid = std::get_if<RoomId>(&loc); if (!rid) return false; 250 251 auto r = p.gc->GetRoomColl().Get<IdKey>(*rid); if (!r) return false; 252 253 return IfInGroupCommon(p, p.gc->GetRoomGroupColl(), r->GetGroupId(), 254 p.check.GetParam1(), "None"); 255 } 256 257 TEST_CASE_FIXTURE(Fixture, "IfObjectInRoomGroup") 258 { 259 dummy.game.GetRooms()[1].SetGroupId(0); 260 261 SetCk("nosuch", "None", ""); 262 CHECK(IfObjectInRoomGroup(p) == false); 263 SetCk("00000000-000b-1ec1-0000-ffffffffffff", "None", ""); 264 CHECK(IfObjectInRoomGroup(p) == false); 265 SetCk("00000000-000b-1ec1-0000-000000000000", "None", ""); 266 CHECK(IfObjectInRoomGroup(p) == false); 267 st.GetObjectColl().At(0).SetLocation(st.GetRoomColl().At(1).GetId()); 268 CHECK(IfObjectInRoomGroup(p) == true); // room 1 has no group 269 SetCk("00000000-000b-1ec1-0000-000000000000", "room_group_0", ""); 270 CHECK(IfObjectInRoomGroup(p) == false); 271 272 st.GetObjectColl().At(0).SetLocation(st.GetRoomColl().At(0).GetId()); 273 SetCk("00000000-000b-1ec1-0000-000000000000", "None", ""); 274 CHECK(IfObjectInRoomGroup(p) == false); // room 0 is in group 0 275 SetCk("00000000-000b-1ec1-0000-000000000000", "room_group_0", ""); 276 CHECK(IfObjectInRoomGroup(p) == true); 277 278 st.GetObjectColl().At(0).SetLocation(st.GetRoomColl().At(2).GetId()); 279 SetCk("00000000-000b-1ec1-0000-000000000000", "None", ""); 280 CHECK(IfObjectInRoomGroup(p) == false); // room 2 is in group 2 in group 0 281 SetCk("00000000-000b-1ec1-0000-000000000000", "room_group_0", ""); 282 CHECK(IfObjectInRoomGroup(p) == true); 283 } 284 285 // --------------------------------------------------------------------------- 286 287 std::optional<bool> IfObjectNotInObject(IfParam p) 288 { return IfObjectInObjectCommon(p, true); } 289 290 TEST_CASE_FIXTURE(Fixture, "IfObjectNotInObject") 291 { 292 SetCk("nosuch", "nosuch", ""); 293 CHECK(IfObjectNotInObject(p) == false); 294 295 SetCk("object_0", "nosuch", ""); 296 CHECK(IfObjectNotInObject(p) == true); 297 SetCk("object_0", "00000000-000b-1ec1-0000-ffffffffffff", ""); 298 CHECK(IfObjectNotInObject(p) == true); 299 SetCk("object_0", "00000000-000b-1ec1-0000-000000000001", ""); 300 CHECK(IfObjectNotInObject(p) == true); 301 st.GetObjectColl().At(0).SetLocation(st.GetObjectColl().At(1).GetId()); 302 CHECK(IfObjectNotInObject(p) == false); 303 // TODO case sensitive 304 // Set("object_0", "00000000-000b-1EC1-0000-000000000001", ""); 305 // CHECK(IfObjectNotInObject(p) == true); 306 307 st.GetObjectColl().At(1).SetLocation(st.GetObjectColl().At(2).GetId()); 308 SetCk("object_0", "00000000-000b-1ec1-0000-000000000002", ""); 309 CHECK(IfObjectNotInObject(p) == true); // not recursive 310 } 311 312 // --------------------------------------------------------------------------- 313 314 std::optional<bool> IfObjectNotInPlayer(IfParam p) 315 { 316 auto o = GetObject(p, p.check.GetParam0()); if (!o) return false; 317 auto loc = o->GetLocation(); 318 auto cid = std::get_if<CharacterId>(&loc); if (!cid) return true; 319 return *cid != p.gc->GetPlayerId(); 320 } 321 322 TEST_CASE_FIXTURE(Fixture, "IfObjectNotInPlayer") 323 { 324 SetCk("nosuch", "", ""); 325 CHECK(IfObjectNotInPlayer(p) == false); 326 SetCk("Object_0", "", ""); 327 CHECK(IfObjectNotInPlayer(p) == true); 328 st.GetObjectColl().At(0).SetLocation(st.GetPlayerId()); 329 CHECK(IfObjectNotInPlayer(p) == false); 330 331 st.GetObjectColl().At(1).SetLocation(st.GetObjectColl().At(0).GetId()); 332 SetCk("object_1", "", ""); 333 CHECK(IfObjectNotInPlayer(p) == true); // not recursive... 334 } 335 336 // --------------------------------------------------------------------------- 337 338 namespace 339 { 340 struct State 341 { 342 Libshit::NonowningString key; 343 bool neg; 344 bool (ConstObjectProxy::*fun)() const; 345 constexpr operator Libshit::NonowningString() const noexcept { return key; } 346 }; 347 } 348 349 static constexpr const State STATES[] = { 350 { "Closed", true, &ConstObjectProxy::GetOpen }, 351 { "Invisible", true, &ConstObjectProxy::GetVisible }, 352 { "Locked", false, &ConstObjectProxy::GetLocked }, 353 { "Open", false, &ConstObjectProxy::GetOpen }, 354 { "Read", false, &ConstObjectProxy::GetRead }, 355 { "Removed", true, &ConstObjectProxy::GetWorn }, 356 { "Unlocked", true, &ConstObjectProxy::GetLocked }, 357 { "Unread", true, &ConstObjectProxy::GetRead }, 358 { "Visible", false, &ConstObjectProxy::GetVisible }, 359 { "Worn", false, &ConstObjectProxy::GetWorn }, 360 }; 361 static_assert(IsSortedAry(STATES, std::less<Libshit::NonowningString>{})); 362 363 std::optional<bool> IfObjectState(IfParam p) 364 { 365 auto o = GetObject(p, p.check.GetParam0()); if (!o) return false; 366 auto key = p.Replace(p.eval.tmp_str0, p.check.GetParam1()); 367 368 auto it = BinarySearch(std::begin(STATES), std::end(STATES), key, 369 std::less<Libshit::NonowningString>{}); 370 if (it == std::end(STATES)) return {}; 371 372 return ((*o).*(it->fun))() ^ it->neg; 373 } 374 375 TEST_CASE_FIXTURE(Fixture, "IfObjectState") 376 { 377 SetCk("nosuch", "nosuch", ""); 378 CHECK(IfObjectState(p) == false); 379 380 SetCk("00000000-000b-1ec1-0000-000000000000", "nosuch", ""); 381 CHECK(IfObjectState(p) == std::nullopt); 382 383 SetCk("object_0", "nosuch", ""); 384 CHECK(IfObjectState(p) == std::nullopt); 385 386 auto check = [&](const char* str, const char* str_neg, bool res) 387 { 388 CAPTURE(str); CAPTURE(str_neg); 389 SetCk("object_0", str, ""); 390 CHECK(IfObjectState(p) == res); 391 SetCk("object_0", str_neg, ""); 392 CHECK(IfObjectState(p) == !res); 393 }; 394 check("Open", "Closed", true); 395 check("Locked", "Unlocked", false); 396 check("Worn", "Removed", true); 397 check("Read", "Unread", false); 398 check("Visible", "Invisible", false); 399 } 400 401 TEST_SUITE_END(); 402 }