From c2d0ac4a618fee29a0ff4fdf56d1d36d09f7af0f Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 2 Oct 2018 09:05:32 +1000 Subject: [PATCH] Converted ItemManager to use a vector of *ItemState (instead of *Item) for its item handling. This allows us later to use the switch functions to work on either the current items or on the confirmed item state (i.e. code reusage for rewind). --- src/items/item.cpp | 12 ++-- src/items/item.hpp | 100 +++++++++++++++++++-------- src/items/item_manager.cpp | 24 +++++-- src/items/item_manager.hpp | 25 ++++--- src/karts/controller/skidding_ai.cpp | 42 +++++------ src/karts/controller/skidding_ai.hpp | 28 ++++---- src/karts/controller/test_ai.cpp | 22 +++--- src/karts/controller/test_ai.hpp | 18 ++--- 8 files changed, 172 insertions(+), 99 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index c0b375d53..4063c665e 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -287,18 +287,21 @@ void Item::setType(ItemType type) void Item::switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh) { setMesh(mesh, lowmesh); - ItemState::switchTo(type); + ItemState::switchTo(type, mesh, lowmesh); } // switchTo //----------------------------------------------------------------------------- -/** Switch backs to the original item. +/** Switch backs to the original item. Returns true if the item wa snot + * actually switched (e.g. trigger, or bubblegum dropped during switch + * time). The return value is not actually used, but necessary in order + * to overwrite ItemState::switchBack() */ -void Item::switchBack() +bool Item::switchBack() { setMesh(m_original_mesh, m_original_lowmesh); if (ItemState::switchBack()) - return; + return true; if (m_node != NULL) { @@ -306,6 +309,7 @@ void Item::switchBack() hpr.setHPR(m_original_rotation); m_node->setRotation(hpr.toIrrHPR()); } + return false; } // switchBack //----------------------------------------------------------------------------- diff --git a/src/items/item.hpp b/src/items/item.hpp index 9e7bb20a8..7aca6887d 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -132,6 +132,24 @@ protected: friend class NetworkItemManager; // ------------------------------------------------------------------------ virtual void setType(ItemType type) { m_type = type; } + // ------------------------------------------------------------------------ + // Some convenient functions for the AI only + friend class SkiddingAI; + friend class TestAI; + /** Returns true if the specified line segment would come close enough + * to this item so that this item would be collected. + * \param line The line segment which is tested if it is close enough + * to this item so that this item would be collected. + */ + bool hitLine(const core::line3df &line, + const AbstractKart *kart = NULL) const + { + if (getPreviousOwner() == kart && getDeactivatedTicks() > 0) + return false; + + Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector()); + return hitKart(closest, kart); + } // hitLine public: ItemState(ItemType type, const AbstractKart *owner=NULL, int id = -1); @@ -143,6 +161,48 @@ public: virtual ~ItemState() {} // ----------------------------------------------------------------------- + /** Dummy implementation, causing an abort if it should be called to + * catch any errors early. */ + virtual void updateGraphics(float dt) + { + Log::fatal("ItemState", "updateGraphics() called for ItemState."); + } // updateGraphics + + // ----------------------------------------------------------------------- + virtual bool hitKart(const Vec3 &xyz, + const AbstractKart *kart = NULL) const + { + Log::fatal("ItemState", "hitKart() called for ItemState."); + return false; + } // hitKart + // ----------------------------------------------------------------------- + virtual bool isPredicted() const + { + Log::fatal("ItemState", "isPredicted() called for ItemState."); + return false; + } // isPredicted + // ----------------------------------------------------------------------- + virtual int getGraphNode() const + { + Log::fatal("ItemState", "getGraphNode() called for ItemState."); + return 0; + } // getGraphNode + // ----------------------------------------------------------------------- + const Vec3 *getAvoidancePoint(bool left) const + { + Log::fatal("ItemState", "getAvoidancePoint() called for ItemState."); + // Return doesn't matter, fatal aborts + return &m_xyz; + } // getAvoidancePoint + // ----------------------------------------------------------------------- + float getDistanceFromCenter() const + { + Log::fatal("itemState", + "getDistanceFromCentre() called for ItemState."); + return 0; + } // getDistanceFromCentre + // ----------------------------------------------------------------------- + /** Resets an item to its start state. */ void reset() { m_deactive_ticks = 0; @@ -156,22 +216,27 @@ public: } } // reset - // ------------------------------------------------------------------------ + // ----------------------------------------------------------------------- /** Switches an item to be of a different type. Used for the switch * powerup. * \param type New type for this item. + * \param mesh Ignored. + * \param lowmesh Ignored. */ - void switchTo(ItemType type) + virtual void switchTo(ItemType type, scene::IMesh *mesh, + scene::IMesh *lowmesh) { // triggers and easter eggs should not be switched if (m_type == ITEM_TRIGGER || m_type == ITEM_EASTER_EGG) return; m_original_type = m_type; setType(type); + return; } // switchTo + // ------------------------------------------------------------------------ /** Returns true if this item was not actually switched (e.g. trigger etc) */ - bool switchBack() + virtual bool switchBack() { // triggers should not be switched if (m_type == ITEM_TRIGGER) return true; @@ -311,12 +376,12 @@ public: Item(const Vec3& xyz, float distance, TriggerItemListener* trigger); virtual ~Item (); - void updateGraphics(float dt); + virtual void updateGraphics(float dt) OVERRIDE; virtual void collected(const AbstractKart *kart) OVERRIDE; void reset(); void switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh); - void switchBack(); + virtual bool switchBack() OVERRIDE; // ------------------------------------------------------------------------ /** Returns true if the Kart is close enough to hit this item, the item is @@ -326,7 +391,7 @@ public: * \param xyz Location of kart (avoiding to use kart->getXYZ() so that * kart.hpp does not need to be included here). */ - bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const + virtual bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const { if (getPreviousOwner() == kart && getDeactivatedTicks() > 0) return false; @@ -336,35 +401,16 @@ public: return lc.length2() < m_distance_2; } // hitKart -protected: - // ------------------------------------------------------------------------ - // Some convenient functions for the AI only - friend class SkiddingAI; - friend class TestAI; - /** Returns true if the specified line segment would come close enough - * to this item so that this item would be collected. - * \param line The line segment which is tested if it is close enough - * to this item so that this item would be collected. - */ - bool hitLine(const core::line3df &line, - const AbstractKart *kart=NULL) const - { - if (getPreviousOwner() == kart && getDeactivatedTicks() > 0) - return false; - - Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector()); - return hitKart(closest, kart); - } // hitLine public: // ------------------------------------------------------------------------ /** Sets if this is a predicted item or not. */ void setPredicted(bool p) { m_is_predicted = p; } // ------------------------------------------------------------------------ /** Returns if this item is predicted or not. */ - bool isPredicted() const { return m_is_predicted; } + virtual bool isPredicted() const OVERRIDE { return m_is_predicted; } // ------------------------------------------------------------------------ /** Returns the index of the graph node this item is on. */ - int getGraphNode() const { return m_graph_node; } + virtual int getGraphNode() const OVERRIDE { return m_graph_node; } // ------------------------------------------------------------------------ /** Returns the distance from center: negative means left of center, * positive means right of center. */ diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index cec02434f..157668fcd 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -460,8 +460,8 @@ void ItemManager::update(int ticks) m_switch_ticks -= ticks; if(m_switch_ticks<0) { - for(AllItemTypes::iterator i =m_all_items.begin(); - i!=m_all_items.end(); i++) + for(AllItemTypes::iterator i = m_all_items.begin(); + i!= m_all_items.end(); i++) { if(*i) (*i)->switchBack(); } // for m_all_items @@ -501,7 +501,7 @@ void ItemManager::updateGraphics(float dt) * items, and then frees the item itself. * \param The item to delete. */ -void ItemManager::deleteItem(Item *item) +void ItemManager::deleteItem(ItemState *item) { // First check if the item needs to be removed from the items-in-quad list if(m_items_in_quads) @@ -523,12 +523,21 @@ void ItemManager::deleteItem(Item *item) //----------------------------------------------------------------------------- /** Switches all items: boxes become bananas and vice versa for a certain - * amount of time (as defined in stk_config.xml. + * amount of time (as defined in stk_config.xml). */ void ItemManager::switchItems() { - for(AllItemTypes::iterator i =m_all_items.begin(); - i!=m_all_items.end(); i++) + switchItemsInternal(m_all_items); +} // switchItems + +//----------------------------------------------------------------------------- +/** Switches all items: boxes become bananas and vice versa for a certain + * amount of time (as defined in stk_config.xml). + */ +void ItemManager::switchItemsInternal(std::vector &all_items) +{ + for(AllItemTypes::iterator i = m_all_items.begin(); + i != m_all_items.end(); i++) { if(!*i) continue; @@ -546,7 +555,8 @@ void ItemManager::switchItems() if (new_type == (*i)->getType()) continue; if(m_switch_ticks<0) - (*i)->switchTo(new_type, m_item_mesh[(int)new_type], m_item_lowres_mesh[(int)new_type]); + (*i)->switchTo(new_type, m_item_mesh[(int)new_type], + m_item_lowres_mesh[(int)new_type]); else (*i)->switchBack(); } // for m_all_items diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index 26614eebf..115d2f3de 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -99,7 +99,7 @@ public: // ======================================================================== protected: /** The vector of all items of the current track. */ - typedef std::vector AllItemTypes; + typedef std::vector AllItemTypes; AllItemTypes m_all_items; private: @@ -111,14 +111,16 @@ private: /** What item this item is switched to. */ std::vector m_switch_to; +protected: /** Remaining time that items should remain switched. If the * value is <0, it indicates that the items are not switched atm. */ int m_switch_ticks; -protected: - void deleteItem(Item *item); + void deleteItem(ItemState *item); virtual unsigned int insertItem(Item *item); + void switchItemsInternal(std::vector < ItemState*> &all_items); void setSwitchItems(const std::vector &switch_items); + ItemManager(); public: virtual ~ItemManager(); @@ -134,7 +136,7 @@ public: void checkItemHit (AbstractKart* kart); void reset (); virtual void collectedItem (ItemState *item, AbstractKart *kart); - void switchItems (); + virtual void switchItems (); bool randomItemsForArena(const AlignedArray& pos); // ------------------------------------------------------------------------ /** Only used in the NetworkItemManager. */ @@ -151,10 +153,16 @@ public: } // ------------------------------------------------------------------------ /** Returns a pointer to the n-th item. */ - const Item* getItem(unsigned int n) const { return m_all_items[n]; }; + const ItemState* getItem(unsigned int n) const + { + return dynamic_cast(m_all_items[n]); + }; // ------------------------------------------------------------------------ /** Returns a pointer to the n-th item. */ - Item* getItem(unsigned int n) { return m_all_items[n]; }; + ItemState* getItem(unsigned int n) + { + return dynamic_cast(m_all_items[n]); + } // ------------------------------------------------------------------------ /** Returns a reference to the array of all items on the specified quad. */ @@ -171,8 +179,9 @@ public: { assert(m_items_in_quads); assert(n < m_items_in_quads->size()); - return ((*m_items_in_quads)[n]).empty() ? NULL : - (*m_items_in_quads)[n].front(); + return ((*m_items_in_quads)[n]).empty() + ? NULL + : dynamic_cast((*m_items_in_quads)[n].front()); } // getFirstItemInQuad }; // ItemManager diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 26bb42670..5a1c97407 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -553,8 +553,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, int node = m_track_node; float distance = 0; - std::vector items_to_collect; - std::vector items_to_avoid; + std::vector items_to_collect; + std::vector items_to_avoid; // 1) Filter and sort all items close by // ------------------------------------- @@ -562,8 +562,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, while(distance < max_item_lookahead_distance) { int n_index= DriveGraph::get()->getNode(node)->getIndex(); - const std::vector &items_ahead = - ItemManager::get()->getItemsInQuads(n_index); + const std::vector &items_ahead = + ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; i0) { - const Item *item_to_collect = items_to_collect[0]; + const ItemState *item_to_collect = items_to_collect[0]; // Test if we would hit a bad item when aiming at this good item. // If so, don't change the aim. In this case it has already been // ensured that we won't hit the bad item (otherwise steerToAvoid @@ -745,8 +745,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, * to be avoided. * \return True if it would hit any of the bad items. */ -bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, - const std::vector &items_to_avoid) +bool SkiddingAI::hitBadItemWhenAimAt(const ItemState *item, + const std::vector &items_to_avoid) { core::line3df to_item(m_kart->getXYZ().toIrrVector(), item->getXYZ().toIrrVector()); @@ -805,7 +805,7 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) * into account). * \return True if steering is necessary to avoid an item. */ -bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, +bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, const core::line3df &line_to_target, Vec3 *aim_point) { @@ -958,9 +958,9 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, * (NULL if no item was avoided so far). * \param item_to_collect A pointer to a previously selected item to collect. */ -void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, - std::vector *items_to_avoid, - std::vector *items_to_collect) +void SkiddingAI::evaluateItems(const ItemState *item, Vec3 kart_aim_direction, + std::vector *items_to_avoid, + std::vector *items_to_collect) { const KartProperties *kp = m_kart->getKartProperties(); @@ -1032,7 +1032,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // Now insert the item into the sorted list of items to avoid // (or to collect). The lists are (for now) sorted by distance - std::vector *list; + std::vector *list; if(avoid) list = items_to_avoid; else @@ -1146,8 +1146,8 @@ void SkiddingAI::handleItems(const float dt, const Vec3 *aim_point, int last_nod int node = m_track_node; float distance = 0; - std::vector items_to_collect; - std::vector items_to_avoid; + std::vector items_to_collect; + std::vector items_to_avoid; // 1) Filter and sort all items close by // ------------------------------------- @@ -1155,7 +1155,7 @@ void SkiddingAI::handleItems(const float dt, const Vec3 *aim_point, int last_nod while(distance < max_item_lookahead_distance) { int n_index= DriveGraph::get()->getNode(node)->getIndex(); - const std::vector &items_ahead = + const std::vector &items_ahead = ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; i &items_to_collect, - const std::vector &items_to_avoid) +void SkiddingAI::handleBubblegum(int item_skill, + const std::vector &items_to_collect, + const std::vector &items_to_avoid) { float shield_radius = m_ai_properties->m_shield_incoming_radius; @@ -1722,8 +1723,9 @@ void SkiddingAI::handleSwatter(int item_skill) * \param items_to_collect The list of close good items * \param items_to_avoid The list of close bad items */ -void SkiddingAI::handleSwitch(int item_skill, const std::vector &items_to_collect, - const std::vector &items_to_avoid) +void SkiddingAI::handleSwitch(int item_skill, + const std::vector &items_to_collect, + const std::vector &items_to_avoid) { // It's extremely unlikely two switches are used close one after another if(item_skill == 2) @@ -2212,7 +2214,7 @@ void SkiddingAI::handleNitroAndZipper(float max_safe_speed) // just keep enough to help accelerating after an accident if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) { - energy_reserve = std::min(2, energy_reserve); + energy_reserve = std::min(2.0f, energy_reserve); } // Don't use nitro or zipper if we are braking diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 8895d9240..8718df32e 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -50,7 +50,7 @@ #include -class Item; +class ItemState; class LinearWorld; class Track; @@ -178,7 +178,7 @@ private: unsigned int m_last_direction_node; /** If set an item that the AI should aim for. */ - const Item *m_item_to_collect; + const ItemState *m_item_to_collect; /** True if items to avoid are close by. Used to avoid using zippers * (which would make it more difficult to avoid items). */ @@ -209,7 +209,7 @@ private: /** The last item selected for collection, for which a probability * was determined. */ - const Item *m_last_item_random; + const ItemState *m_last_item_random; /** True if m_last_item_random was randomly selected to be collected. */ bool m_really_collect_item; @@ -256,13 +256,15 @@ private: int computeSkill(SkillType type); void handleItems(const float dt, const Vec3 *aim_point, int last_node, int item_skill); - void handleBubblegum(int item_skill, const std::vector &items_to_collect, - const std::vector &items_to_avoid); + void handleBubblegum(int item_skill, + const std::vector &items_to_collect, + const std::vector &items_to_avoid); void handleCake(int item_skill); void handleBowling(int item_skill); void handleSwatter(int item_skill); - void handleSwitch(int item_skill, const std::vector &items_to_collect, - const std::vector &items_to_avoid); + void handleSwitch(int item_skill, + const std::vector &items_to_collect, + const std::vector &items_to_avoid); void handleRescue(const float dt); void handleBraking(float max_turn_speed, float min_speed); void handleNitroAndZipper(float max_safe_speed); @@ -270,14 +272,14 @@ private: void handleItemCollectionAndAvoidance(Vec3 *aim_point, int last_node); bool handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point); - bool steerToAvoid(const std::vector &items_to_avoid, + bool steerToAvoid(const std::vector &items_to_avoid, const core::line3df &line_to_target, Vec3 *aim_point); - bool hitBadItemWhenAimAt(const Item *item, - const std::vector &items_to_avoid); - void evaluateItems(const Item *item, Vec3 kart_aim_direction, - std::vector *items_to_avoid, - std::vector *items_to_collect); + bool hitBadItemWhenAimAt(const ItemState *item, + const std::vector &items_to_avoid); + void evaluateItems(const ItemState *item, Vec3 kart_aim_direction, + std::vector *items_to_avoid, + std::vector *items_to_collect); void checkCrashes(const Vec3& pos); void findNonCrashingPointNew(Vec3 *result, int *last_node); diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index f90ef8086..445393e14 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -631,8 +631,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, int node = m_track_node; float distance = 0; - std::vector items_to_collect; - std::vector items_to_avoid; + std::vector items_to_collect; + std::vector items_to_avoid; // 1) Filter and sort all items close by // ------------------------------------- @@ -640,7 +640,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, while(distance < max_item_lookahead_distance) { int n_index= DriveGraph::get()->getNode(node)->getIndex(); - const std::vector &items_ahead = + const std::vector &items_ahead = ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; i0) { - const Item *item_to_collect = items_to_collect[0]; + const ItemState *item_to_collect = items_to_collect[0]; // Test if we would hit a bad item when aiming at this good item. // If so, don't change the aim. In this case it has already been // ensured that we won't hit the bad item (otherwise steerToAvoid @@ -823,8 +823,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, * to be avoided. * \return True if it would hit any of the bad items. */ -bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, - const std::vector &items_to_avoid) +bool SkiddingAI::hitBadItemWhenAimAt(const ItemState *item, + const std::vector &items_to_avoid) { core::line3df to_item(m_kart->getXYZ().toIrrVector(), item->getXYZ().toIrrVector()); @@ -883,7 +883,7 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) * into account). * \return True if steering is necessary to avoid an item. */ -bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, +bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, const core::line3df &line_to_target, Vec3 *aim_point) { @@ -1036,9 +1036,9 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, * (NULL if no item was avoided so far). * \param item_to_collect A pointer to a previously selected item to collect. */ -void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, - std::vector *items_to_avoid, - std::vector *items_to_collect) +void SkiddingAI::evaluateItems(const ItemState *item, Vec3 kart_aim_direction, + std::vector *items_to_avoid, + std::vector *items_to_collect) { const KartProperties *kp = m_kart->getKartProperties(); @@ -1110,7 +1110,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // Now insert the item into the sorted list of items to avoid // (or to collect). The lists are (for now) sorted by distance - std::vector *list; + std::vector *list; if(avoid) list = items_to_avoid; else diff --git a/src/karts/controller/test_ai.hpp b/src/karts/controller/test_ai.hpp index d9b8615fd..cc7a83497 100644 --- a/src/karts/controller/test_ai.hpp +++ b/src/karts/controller/test_ai.hpp @@ -51,7 +51,7 @@ #include -class Item; +class ItemState; #ifdef AI_DEBUG class ShowCurve; @@ -131,7 +131,7 @@ private: unsigned int m_last_direction_node; /** If set an item that the AI should aim for. */ - const Item *m_item_to_collect; + const ItemState *m_item_to_collect; /** True if items to avoid are close by. Used to avoid using zippers * (which would make it more difficult to avoid items). */ @@ -156,7 +156,7 @@ private: /** The last item selected for collection, for which a probability * was determined. */ - const Item *m_last_item_random; + const ItemState *m_last_item_random; /** True if m_last_item_random was randomly selected to be collected. */ bool m_really_collect_item; @@ -210,14 +210,14 @@ private: void handleItemCollectionAndAvoidance(Vec3 *aim_point, int last_node); bool handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point); - bool steerToAvoid(const std::vector &items_to_avoid, + bool steerToAvoid(const std::vector &items_to_avoid, const core::line3df &line_to_target, Vec3 *aim_point); - bool hitBadItemWhenAimAt(const Item *item, - const std::vector &items_to_avoid); - void evaluateItems(const Item *item, Vec3 kart_aim_direction, - std::vector *items_to_avoid, - std::vector *items_to_collect); + bool hitBadItemWhenAimAt(const ItemState *item, + const std::vector &items_to_avoid); + void evaluateItems(const ItemState *item, Vec3 kart_aim_direction, + std::vector *items_to_avoid, + std::vector *items_to_collect); void checkCrashes(const Vec3& pos); void findNonCrashingPointFixed(Vec3 *result, int *last_node);