-
Notifications
You must be signed in to change notification settings - Fork 278
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
refactor!: give items identity #2250
Changes from 127 commits
a12b47e
ad0ef91
454ca13
24b3ddd
16e53ef
2380410
944cc0f
a2b39d7
d60311c
6b70aa5
7510577
713af91
0da8198
1110027
5d5897a
6bcf8eb
60c2393
50518b2
3553401
0b12953
18112e1
bcfe831
767c545
189100b
0209ed3
6d6a28f
4bd3a36
c82307f
8c1c36a
cd3aa3d
c5ea05a
4b624a5
b9c775c
a40f45f
f0b724c
74a2366
7138c39
0fe91b6
23d1af1
1567f4a
2225190
e9f6c96
075d984
0a196b6
9514ef2
0e55826
5490d6c
a3018d2
a1d4fdc
f6fef39
94efd3b
a1a6935
23c81f8
c320530
4c6c5f2
d6f27b0
582bf36
964b03b
5cf4e29
3b1678f
312d122
ae159f1
d42f9c5
7775c98
4ed4d17
a05a51c
fe65d83
e2708b5
d2017f1
d7ce52f
72dcc85
a57239f
b319e78
3e5ccaa
6fcf9f8
64bccc0
f856c69
7b8782f
eb44b19
bddeec4
fe9fe2f
fc8daca
d222b27
3292b58
6689a63
cbb5bfd
c107a27
7fd408d
cc3137b
daaffe1
10d1eeb
1dc2409
867f96e
152c389
b26ea16
a4e599f
fc35e9a
b5c73b7
2d07409
97054bb
8bf8a5d
cf2510f
8edf510
1e92947
2f1bc14
c2f88d3
fddf2c5
945a5fe
1c9190d
1ada61e
29acbc7
1d0205f
8118233
05249cf
3e7373d
277c068
1f3dbe1
000c5a0
afc06ac
cb6ee3a
82fbdab
b1075e7
f76b178
1f67b88
a1e2bdd
7c0f646
ba4e860
91d2a78
360d121
5c371ed
8b574d4
0f04a09
1e4f856
b06506d
2d1b37f
09d3f4d
5531a5a
3a344ae
6fc592a
7d94ff7
6d85c7a
0aee3c8
a3b3b47
d1d29c4
f87a168
e6eb11a
e5110a6
e9a4cc5
bee0ad4
f6d0256
59afa89
c25aa04
30cd47a
9dfd4c8
76b1b4f
e519498
e0dc0bb
0b4b6a5
b360544
c3fc753
edbf095
283f7b4
6ca5680
c0b956b
9ef4863
5a173ee
afe72e5
4e3bb46
113373c
b27bd0a
a75d782
56d4229
67c0dfc
b6725c2
d2069d7
6d170bc
f94b7f1
8144c81
bfec42c
02fab74
9f75cca
9114851
791acff
c524884
c1a4e8a
d84aa0a
a05cd40
9548bbb
965c201
fdcac04
fc5f888
d00cbc9
8a36c8b
484b43b
adc87f9
f24486e
38c027f
8bc16fb
e2e7884
09fc616
e80a4bc
26e44a4
c73e1c4
27df241
c08c1f6
3577fe1
13a0587
0df5689
80cbc96
14a353d
6bd1991
3517cfa
b72697c
c78faaf
6d79737
bd65e2a
e6a7268
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Game Objects | ||
|
||
Many of the things that physically exist within the game world are **game objects(GO)**. Currently | ||
this only covers items, but creatures and vehicles are coming soon and then probably furniture at | ||
some point. GOs have a small common interface with methods like name and position that you can find | ||
in [`game_object.h`](../src/game_object.h). GOs use private constructors, this means **your | ||
variables must always be references or pointers**. Trying to assign a variable without a layer of | ||
indirection like that will cause a compile error. Likewise trying to copy one object over another | ||
will cause a similar error. | ||
|
||
```c++ | ||
item& it_ref = ...; // Good | ||
item* it_pointer; // Good | ||
|
||
item it = ...; // Compile error | ||
it_ref = other; // Compile error | ||
*it_pointer = other; // Compile error | ||
``` | ||
|
||
New GOs can be created via `::spawn` static methods that return a `detached_ptr` to the newly | ||
created game object. A `detached_ptr` represents an object that does not currently exist in the game | ||
world. There can only be one `detached_ptr` to an object at a time (those who know | ||
[`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr) will be familiar with this | ||
behavior). Functions that add an object to the world will require that you `std::move` in a | ||
`detached_ptr`. In general `detached_ptr`s must be `std::move`'d when they're passed into a | ||
function. Note that you should never use any variable after it has been moved. Likewise functions | ||
that remove an object from the world will return a `detached_ptr`. This ensures that you can't add | ||
the same thing to the world twice. | ||
|
||
Functions that don't add things to the world just accept normal pointers. You can turn a | ||
`detached_ptr` into a normal pointer by doing this. Note that you can use this to keep a valid | ||
pointer to something even after you `std::move` it, but you must do this before the `std::move` | ||
happens. | ||
|
||
```c++ | ||
&*detached | ||
``` | ||
|
||
And you can go the other way using this. Though note that it removes the object from the game world | ||
in the process and will cause errors if called on an object that isn't in the game world. | ||
|
||
```c++ | ||
detached_ptr<item> as_detached=normal_ptr->detach(); | ||
``` | ||
|
||
Trying to access an invalid `detached_ptr` (for instance one that has been `std::move`'d from) will | ||
cause a debugmsg and give you the null version of that object. | ||
|
||
## Safe References | ||
|
||
Game objects support safe references. `safe_reference<item>` will refuse access to an object that | ||
has been destroyed or is outside of the reality bubble without losing track of it, and they can be | ||
saved and loaded. You must check them as a boolean (e.g. `if( ref )`) to see if they're valid. | ||
Trying to access them when they're not will cause debugmsgs and give you a null object instead. They | ||
have a small interface that lets you check if they're destroyed or unloaded etc. If they were | ||
destroyed or unloaded this turn, they have a method that will allow you to access the object in a | ||
const fashion, for instance to display an error message. | ||
|
||
If you're moving objects around you need to use `detached_ptr`s, but otherwise when choosing which | ||
reference to use the most important thing to consider is how long you want to hold onto it. If | ||
you're just using something temporarily, for instance most arguments and variables, you should use a | ||
normal reference or pointer. If you need to store it for longer you should use a safe reference and | ||
this means it can be easily stored in the save file. In the rare case that you do want to save it | ||
across turns but don't want to store it in the save file, which means caches, there's also a fast | ||
`cache_reference<item>`, which does last across turns but can't be saved. | ||
|
||
Game objects can sometimes be split into pieces or merged together. Item stacks are the main example | ||
of this but there are others like vehicles being split or dissoluted devourers merging. When a stack | ||
of items is split, the stack that stays in place is the one that safe references will follow. When | ||
they are merged safe references to either half of the merge will now point to the merge result. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,4 +55,3 @@ | |
#include <unordered_set> | ||
#include <utility> | ||
#include <vector> | ||
#include "../src/colony.h" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,62 +9,58 @@ | |
void active_item_cache::remove( const item *it ) | ||
{ | ||
for( auto &list : active_items ) { | ||
list.second.remove_if( [it]( const item_reference & active_item ) { | ||
item *const target = active_item.item_ref.get(); | ||
list.second.erase( std::remove_if( list.second.begin(), | ||
list.second.end(), [it]( const cache_reference<item> &active_item ) { | ||
if( !active_item ) { | ||
return true; | ||
} | ||
item *const target = &*active_item; | ||
return !target || target == it; | ||
} ); | ||
} ), list.second.end() ); | ||
} | ||
if( it->can_revive() ) { | ||
special_items[ special_item_type::corpse ].remove_if( [it]( const item_reference & active_item ) { | ||
item *const target = active_item.item_ref.get(); | ||
return !target || target == it; | ||
} ); | ||
std::vector<cache_reference<item>> &corpse = special_items[ special_item_type::corpse ]; | ||
corpse.erase( std::remove( corpse.begin(), corpse.end(), it ), corpse.end() ); | ||
} | ||
if( it->get_use( "explosion" ) ) { | ||
special_items[ special_item_type::explosive ].remove_if( [it]( const item_reference & | ||
active_item ) { | ||
item *const target = active_item.item_ref.get(); | ||
return !target || target == it; | ||
} ); | ||
std::vector<cache_reference<item>> &explosive = special_items[ special_item_type::explosive ]; | ||
explosive.erase( std::remove( explosive.begin(), explosive.end(), it ), explosive.end() ); | ||
} | ||
} | ||
|
||
void active_item_cache::add( item &it, point location ) | ||
void active_item_cache::add( item &it ) | ||
{ | ||
// If the item is alread in the cache for some reason, don't add a second reference | ||
std::list<item_reference> &target_list = active_items[it.processing_speed()]; | ||
if( std::find_if( target_list.begin(), | ||
target_list.end(), [&it]( const item_reference & active_item_ref ) { | ||
return &it == active_item_ref.item_ref.get(); | ||
} ) != target_list.end() ) { | ||
std::vector<cache_reference<item>> &target_list = active_items[it.processing_speed()]; | ||
if( std::find( target_list.begin(), target_list.end(), it ) != target_list.end() ) { | ||
return; | ||
} | ||
if( it.can_revive() ) { | ||
special_items[ special_item_type::corpse ].push_back( item_reference{ location, it.get_safe_reference() } ); | ||
special_items[ special_item_type::corpse ].push_back( it ); | ||
} | ||
if( it.get_use( "explosion" ) ) { | ||
special_items[ special_item_type::explosive ].push_back( item_reference{ location, it.get_safe_reference() } ); | ||
special_items[ special_item_type::explosive ].push_back( it ); | ||
} | ||
target_list.push_back( item_reference{ location, it.get_safe_reference() } ); | ||
target_list.push_back( it ); | ||
} | ||
|
||
bool active_item_cache::empty() const | ||
{ | ||
for( std::pair<int, std::list<item_reference>> active_queue : active_items ) { | ||
for( std::pair<int, std::vector<cache_reference<item>>> active_queue : active_items ) { | ||
if( !active_queue.second.empty() ) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
std::vector<item_reference> active_item_cache::get() | ||
std::vector<item *> active_item_cache::get() | ||
{ | ||
std::vector<item_reference> all_cached_items; | ||
for( std::pair<const int, std::list<item_reference>> &kv : active_items ) { | ||
for( std::list<item_reference>::iterator it = kv.second.begin(); it != kv.second.end(); ) { | ||
if( it->item_ref ) { | ||
all_cached_items.emplace_back( *it ); | ||
std::vector<item *> all_cached_items; | ||
for( std::pair<const int, std::vector<cache_reference<item>>> &kv : active_items ) { | ||
for( std::vector<cache_reference<item>>::iterator it = kv.second.begin(); it != kv.second.end(); ) { | ||
if( *it ) { | ||
all_cached_items.push_back( & **it ); | ||
++it; | ||
} else { | ||
it = kv.second.erase( it ); | ||
|
@@ -74,16 +70,21 @@ std::vector<item_reference> active_item_cache::get() | |
return all_cached_items; | ||
} | ||
|
||
std::vector<item_reference> active_item_cache::get_for_processing() | ||
std::vector<item *> active_item_cache::get_for_processing() | ||
{ | ||
std::vector<item_reference> items_to_process; | ||
for( std::pair<const int, std::list<item_reference>> &kv : active_items ) { | ||
std::vector<item *> items_to_process; | ||
for( std::pair < const int, std::vector<cache_reference<item>>> &kv : active_items ) { | ||
// Rely on iteration logic to make sure the number is sane. | ||
int num_to_process = kv.second.size() / kv.first; | ||
std::list<item_reference>::iterator it = kv.second.begin(); | ||
std::vector<cache_reference<item>>::iterator it = kv.second.begin(); | ||
|
||
//TODO!: This is a bit of a hack, at the very least check the maths | ||
//Skip the entries that were processed last turn. | ||
it += num_to_process * ( to_turn<int>( calendar::turn ) % kv.first ); | ||
|
||
for( ; it != kv.second.end() && num_to_process >= 0; ) { | ||
if( it->item_ref ) { | ||
items_to_process.push_back( *it ); | ||
if( *it ) { | ||
items_to_process.push_back( & **it ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's some weird behavior with food vanishing. This snippet may be related, possible cause:
The old implementation (commented out a few lines below) worked around this by shuffling processed items to the end of the list, so on the 2nd turn the 2nd item becomes the 1st, and gets updated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another issue with vanishing food:
|
||
--num_to_process; | ||
++it; | ||
} else { | ||
|
@@ -93,34 +94,18 @@ std::vector<item_reference> active_item_cache::get_for_processing() | |
} | ||
// Rotate the returned items to the end of their list so that the items that weren't | ||
// returned this time will be first in line on the next call | ||
kv.second.splice( kv.second.end(), kv.second, kv.second.begin(), it ); | ||
// kv.second.splice( kv.second.end(), kv.second, kv.second.begin(), it ); | ||
} | ||
return items_to_process; | ||
} | ||
|
||
std::vector<item_reference> active_item_cache::get_special( special_item_type type ) | ||
{ | ||
std::vector<item_reference> matching_items; | ||
for( const item_reference &it : special_items[type] ) { | ||
matching_items.push_back( it ); | ||
} | ||
return matching_items; | ||
} | ||
|
||
void active_item_cache::subtract_locations( point delta ) | ||
{ | ||
for( std::pair<const int, std::list<item_reference>> &pair : active_items ) { | ||
for( item_reference &ir : pair.second ) { | ||
ir.location -= delta; | ||
} | ||
} | ||
} | ||
|
||
void active_item_cache::rotate_locations( int turns, point dim ) | ||
std::vector<item *> active_item_cache::get_special( special_item_type type ) | ||
{ | ||
for( std::pair<const int, std::list<item_reference>> &pair : active_items ) { | ||
for( item_reference &ir : pair.second ) { | ||
ir.location = ir.location.rotate( turns, dim ); | ||
std::vector<item *> matching_items; | ||
for( const cache_reference<item> &it : special_items[type] ) { | ||
if( it ) { | ||
matching_items.push_back( &*it ); | ||
} | ||
} | ||
return matching_items; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file seems to be a leftover from migration to new docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah this is a new doc added for the new features so it won't have been caught by the migration.