Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Food rot calculation since start of the apocalypse is wrong #22308

Closed
Firestorm01X2 opened this issue Oct 30, 2017 · 17 comments

Comments

Projects
None yet
10 participants
@Firestorm01X2
Copy link
Contributor

commented Oct 30, 2017

Clermont.zip
Game version:
6899

Operating system:
Win 7 X64

Tiles or curses:
Tiles

Mods active:
Default mods.

Expected behavior

Food starting to rot from beginning of the apocalypse i.e from Spring day 1. So it shouldn't be possible to find, for example non rotten milk on Summer 7.

Actual behavior

I am constantly finding food that supposed to be rotten already. For now I have non rotten milk on Summer 7 that was found in house. I even have non pershed fruits founded in houses.

Steps to reproduce the behavior

  1. Start game from Summer.
  2. Wait or play for some game days.
  3. Try fo find food that was generated recently.
  4. You will see food good food that already should be generated rotten.

I've looked in code at item.cpp. Looks like food rot calculation should use start date of the apocalypse. There is calendar calculation. Probaly something just wrong.

I've attached save with my character on Summer 7 with bottle of milk in hands. Milk is not rotten. But it should be possible. Whole season passed. This is not attached to milk only of course. Other perishable food affected too.

But if food was generated already then it starts to perish correctly.

I suppose by design it should be generated rotten or set rotten on next check if too much time passed. But for some reason it was bugged. Or maybe discarded?

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented Oct 30, 2017

Well I thought about it closely. I encountered this for long time ago. Probably it just as it is supposed to work. Technical limitation. So just close issue if it is so.

@Coolthulhu

This comment has been minimized.

Copy link
Contributor

commented Oct 30, 2017

Was the milk spawned in mapgen or from a creature? Creatures drop fresh items.

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented Oct 30, 2017

Was the milk spawned in mapgen or from a creature? Creatures drop fresh items.

Definitely not from creature. It was found in house. Actually I never saw 1 gallon milk bottle spawned from creature.

Also from my save you can find pancakes under my character that was found in something called Sugar House (or so). So it is definitely not milk or container only related.

@Weyrling

This comment has been minimized.

Copy link

commented Oct 30, 2017

This behavior probably comes from temperature/weather, at the beginning of Spring it's still cold.

If you look into weather.cpp you'll find get_rot_since which I've pasted below for convenience.
This appears to calculate rot based on the weather at the location, if it's still cold then things will rot much more slowly.

int get_rot_since( const int startturn, const int endturn, const tripoint &location )
{
// Ensure food doesn't rot in ice labs, where the
// temperature is much less than the weather specifies.
tripoint const omt_pos = ms_to_omt_copy( location );
oter_id const & oter = overmap_buffer.ter( omt_pos );
// TODO: extract this into a property of the overmap terrain
if (is_ot_type("ice_lab", oter)) {
return 0;
}
// TODO: maybe have different rotting speed when underground?
int ret = 0;
const auto &wgen = g->get_cur_weather_gen();
for (calendar i(startturn); i.get_turn() < endturn; i += 600) {
w_point w = wgen.get_weather( location, i, g->get_seed() );
ret += std::min(600, endturn - i.get_turn()) * get_hourly_rotpoints_at_temp(w.temperature) / 600;
}
return ret;
}

@Coolthulhu

This comment has been minimized.

Copy link
Contributor

commented Oct 30, 2017

I still wouldn't expect milk to last well into summer.
Most rotable things are rotten by start of summer already.

@taiyu-len

This comment has been minimized.

Copy link
Contributor

commented Oct 30, 2017

valgrind does show a lot of use of uninitialized values in rot related functions.
might be an issue

removed most of the repeated stuff. a full log here

==14491== Conditional jump or move depends on uninitialised value(s)
==14491==    at 0x97C6E9: fastfloor(float) (simplexnoise.cpp:471)
==14491==    by 0x97D4E1: raw_noise_4d(float, float, float, float) (simplexnoise.cpp:346)
==14491==    by 0x93CE3B: weather_generator::get_weather(tripoint const&, calendar const&, unsigned int) const (weather_gen.cpp:38)
==14491==    by 0xA69DEF: get_rot_since(int, int, tripoint const&) (weather.cpp:82)
==14491==    by 0x709118: item::calc_rot(tripoint const&) (item.cpp:2891)
==14491==    by 0x6C79A2: map::has_rotten_away(item&, tripoint const&) const (map.cpp:6781)
==14491==    by 0x6FB59A: void map::remove_rotten_items<std::__debug::list<item, std::allocator<item> > >(std::__debug::list<item, std::allocator<item> >&, tripoint const&) (map.cpp:6803)
==14491==    by 0x6DE68F: map::actualize(int, int, int) (map.cpp:7066)
==14491==    by 0x6E3945: map::loadn(int, int, int, bool) (map.cpp:6762)
==14491==    by 0x6E46BA: map::loadn(int, int, bool) (map.cpp:6627)
==14491==    by 0x6E4B08: map::load(int, int, int, bool) (map.cpp:6434)
==14491==    by 0x784D65: game::load_map(tripoint) (game.cpp:790)
...

==14491== Conditional jump or move depends on uninitialised value(s)
==14491==    at 0x97D5A2: raw_noise_4d(float, float, float, float) (simplexnoise.cpp:369)
==14491==    by 0x93CE7E: weather_generator::get_weather(tripoint const&, calendar const&, unsigned int) const (weather_gen.cpp:39)
==14491==    by 0xA69DEF: get_rot_since(int, int, tripoint const&) (weather.cpp:82)
==14491==    by 0x709118: item::calc_rot(tripoint const&) (item.cpp:2891)
==14491==    by 0x6C79A2: map::has_rotten_away(item&, tripoint const&) const (map.cpp:6781)
==14491==    by 0x6FB59A: void map::remove_rotten_items<std::__debug::list<item, std::allocator<item> > >(std::__debug::list<item, std::allocator<item> >&, tripoint const&) (map.cpp:6803)
==14491==    by 0x6DE68F: map::actualize(int, int, int) (map.cpp:7066)
==14491==    by 0x6E3945: map::loadn(int, int, int, bool) (map.cpp:6762)
==14491==    by 0x6E46BA: map::loadn(int, int, bool) (map.cpp:6627)
==14491==    by 0x6E4B08: map::load(int, int, int, bool) (map.cpp:6434)
==14491==    by 0x784D65: game::load_map(tripoint) (game.cpp:790)
==14491==    by 0x5134CE: game::unserialize(std::istream&) (savegame.cpp:202)
...

==14491== Conditional jump or move depends on uninitialised value(s)
==14491==    at 0x93D19F: min<double> (stl_algobase.h:200)
==14491==    by 0x93D19F: weather_generator::get_weather(tripoint const&, calendar const&, unsigned int) const (weather_gen.cpp:72)
==14491==    by 0xA69DEF: get_rot_since(int, int, tripoint const&) (weather.cpp:82)
==14491==    by 0x709118: item::calc_rot(tripoint const&) (item.cpp:2891)
==14491==    by 0x6C79A2: map::has_rotten_away(item&, tripoint const&) const (map.cpp:6781)
==14491==    by 0x6FB59A: void map::remove_rotten_items<std::__debug::list<item, std::allocator<item> > >(std::__debug::list<item, std::allocator<item> >&, tripoint const&) (map.cpp:6803)
==14491==    by 0x6DE68F: map::actualize(int, int, int) (map.cpp:7066)
==14491==    by 0x6E3945: map::loadn(int, int, int, bool) (map.cpp:6762)
==14491==    by 0x6E46BA: map::loadn(int, int, bool) (map.cpp:6627)
==14491==    by 0x6E4B08: map::load(int, int, int, bool) (map.cpp:6434)
==14491==    by 0x784D65: game::load_map(tripoint) (game.cpp:790)
==14491==    by 0x5134CE: game::unserialize(std::istream&) (savegame.cpp:202)
==14491==    by 0x800BED: __invoke_impl<void, void (game::*&)(std::basic_istream<char>&), game*&, std::basic_istream<char, std::char_traits<char> >&> (invoke.h:73)
==14491==    by 0x800BED: __invoke<void (game::*&)(std::basic_istream<char>&), game*&, std::basic_istream<char, std::char_traits<char> >&> (invoke.h:95)
==14491==    by 0x800BED: __call<void, std::basic_istream<char, std::char_traits<char> >&, 0, 1> (functional:467)
==14491==    by 0x800BED: operator()<std::basic_istream<char, std::char_traits<char> >&> (functional:551)
==14491==    by 0x800BED: std::_Function_handler<void (std::istream&), std::_Bind<void (game::*(game*, std::_Placeholder<1>))(std::istream&)> >::_M_invoke(std::_Any_data const&, std::istream&) (std_function.h:316)
...

==14491== Conditional jump or move depends on uninitialised value(s)
==14491==    at 0x4553B7: get_hourly_rotpoints_at_temp(int) (weather_data.cpp:199)
==14491==    by 0xA69D8E: get_rot_since(int, int, tripoint const&) (weather.cpp:83)
==14491==    by 0x709118: item::calc_rot(tripoint const&) (item.cpp:2891)
==14491==    by 0x6C79A2: map::has_rotten_away(item&, tripoint const&) const (map.cpp:6781)
==14491==    by 0x6FB59A: void map::remove_rotten_items<std::__debug::list<item, std::allocator<item> > >(std::__debug::list<item, std::allocator<item> >&, tripoint const&) (map.cpp:6803)
==14491==    by 0x6DE68F: map::actualize(int, int, int) (map.cpp:7066)
==14491==    by 0x6E3945: map::loadn(int, int, int, bool) (map.cpp:6762)
==14491==    by 0x6E46BA: map::loadn(int, int, bool) (map.cpp:6627)
==14491==    by 0x6E4B08: map::load(int, int, int, bool) (map.cpp:6434)
==14491==    by 0x784D65: game::load_map(tripoint) (game.cpp:790)
==14491==    by 0x5134CE: game::unserialize(std::istream&) (savegame.cpp:202)
==14491==    by 0x800BED: __invoke_impl<void, void (game::*&)(std::basic_istream<char>&), game*&, std::basic_istream<char, std::char_traits<char> >&> (invoke.h:73)
==14491==    by 0x800BED: __invoke<void (game::*&)(std::basic_istream<char>&), game*&, std::basic_istream<char, std::char_traits<char> >&> (invoke.h:95)
==14491==    by 0x800BED: __call<void, std::basic_istream<char, std::char_traits<char> >&, 0, 1> (functional:467)
==14491==    by 0x800BED: operator()<std::basic_istream<char, std::char_traits<char> >&> (functional:551)
==14491==    by 0x800BED: std::_Function_handler<void (std::istream&), std::_Bind<void (game::*(game*, std::_Placeholder<1>))(std::istream&)> >::_M_invoke(std::_Any_data const&, std::istream&) (std_function.h:316)
@cainiaowu

This comment has been minimized.

Copy link
Contributor

commented Oct 30, 2017

turn on build in debug msg on will show rotting related info in item info which might help debug this.

@cainiaowu

This comment has been minimized.

Copy link
Contributor

commented Oct 30, 2017

my guess is that the item is generated with bday set to the time when player first enter the map.

@ZhilkinSerg

This comment has been minimized.

Copy link
Contributor

commented Oct 31, 2017

I've got some interesting results. Some of the items have birthday on current turn while others on 0 turn - see tin of corn.

default


Put code to 1.lua in executable folder and run in the console: dofile('./1.lua')

function CheckItem(item, x, y, z, item_searchmask)

  local item_display_name = item:display_name()
  local item_weight = item.type.weight
  local item_volume = item.type.volume:value()
  local item_birthday = item:birthday()
  local item_age = item:age()
  
  if (string.match(item_display_name, item_searchmask) ~= nil) then

    --local message_text = string.format("Item: %s [W:<color_white>%d</color>, V:<color_white>%d</color>] at [X:<color_white>%d</color>, Y:<color_white>%d</color>, Z:<color_white>%d</color>]", item_display_name, item_weight, item_volume, x, y, z)
    local message_text = string.format("Item: %s [B:<color_green>%d</color>, A:<color_cyan>%d</color>] at [<color_red>%d</color>,<color_red>%d</color>,<color_red>%d</color>]", item_display_name, item_birthday, item_age, x, y, z)
    game.add_msg(message_text)
    print(message_text)

  end

end

function CheckMapItems(item_searchmask)

  local REALITY_BUBBLE_WIDTH = 24
  local ScanRadius = REALITY_BUBBLE_WIDTH * 5
  local center = player:pos()
  
  for x = -ScanRadius, ScanRadius do
    for y = -ScanRadius, ScanRadius do
      local z = 0 --only current Z-level
  
      local point = tripoint (center.x + x, center.y + y, center.z + z)
      local distance = game.trig_dist(point.x, point.y, center.x, center.y)
  
      if (distance <= ScanRadius) then
        if map:i_at(point):size() > 0 then
          local item_stack_iterator =  map:i_at(point):cppbegin()
          for _ = 1, map:i_at(point):size() do
            CheckItem(item_stack_iterator:elem(), point.x, point.y, point.z, item_searchmask)
            item_stack_iterator:inc()
          end
        end
      end
  
    end
  end

end

CheckMapItems("fresh")
CheckMapItems("rotten")

local calendar = game.get_calendar_turn()
local calendar_turn = calendar:get_turn()
local message_text = string.format("Calendar turn: <color_yellow>%d</color>", calendar_turn)
print (message_text)
game.add_msg (message_text)
@BorkBorkGoesTheCode

This comment has been minimized.

Copy link
Contributor

commented May 11, 2018

Debug setting the season to autumn from spring: items are randomly fresh when spawned by mapgen.

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented May 28, 2018

my guess is that the item is generated with bday set to the time when player first enter the map.

This guessing is true. Some food indeed has non zero bday in scenarion "Next summer".
But how?

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented May 28, 2018

Ok. How about that:
Cataclysm-DDA\src\mapgen.cpp

 void apply( const mapgendata &dat, const jmapgen_int &x, const jmapgen_int &y, const float /*mon_density*/ ) const override
        {
            if( rng( 0, 99 ) < chance ) {
                const Item_spawn_data *const isd = &result_group;
                const std::vector<item> spawn = isd->create( calendar::turn );
                dat.m.spawn_items( tripoint( rng( x.val, x.valmax ), rng( y.val, y.valmax ), dat.m.get_abs_sub().z ), spawn );
            }
        }

const std::vector<item> spawn = isd->create( **calendar::turn** );

calendar::turn
It is literally set current turn as item birthday.

@BevapDin

This comment has been minimized.

Copy link
Contributor

commented May 28, 2018

Some mapgen code uses calendar::turn as birthday of spawned items (for example jmapgen_liquid_item or jmapgen_loot). Those items are created at the current turn and are therefor considered fresh. Mapgen should create items with birthday calendar::time_of_cataclysm.

Note that the default for new items is the current turn, but the default for items created via map::spawn_item and similar is time_of_cataclysm. Also: mapgen should have a turn parameter that dictates which turn to use as birthday, but it's not forwarded correctly all the time.

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented May 28, 2018

Some mapgen code uses calendar::turn as birthday of spawned items (for example jmapgen_liquid_item or jmapgen_loot). Those items are created at the current turn and are therefor considered fresh. Mapgen should create items with birthday calendar::time_of_cataclysm.

Well. I guess that is it. No surprise that smoe items spawned fresh then.

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented May 28, 2018

That is also mean that most likely rot calculation is ok. It is wrong date for birthday itself.

@Firestorm01X2

This comment has been minimized.

Copy link
Contributor Author

commented Jun 30, 2018

Could someone confirm that bug have been fixed?
It may be not:
https://discourse.cataclysmdda.org/t/food-spoilage-outside-reality-bubble/15442/12?u=firestorm_01

@kevingranade

This comment has been minimized.

Copy link
Member

commented Feb 8, 2019

Inactive for many months, if you run across fresh food at a late date, please open a new issue and list what the food was and where you found it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.