Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 1 | // MorgenGrauen MUDlib |
| 2 | // |
| 3 | // container/vitems.c -- managing virtually present automatic items |
| 4 | // |
Zesstra | 0d64cca | 2020-03-09 21:03:56 +0100 | [diff] [blame] | 5 | #pragma strict_types,rtt_checks, range_check |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 6 | #pragma no_clone |
| 7 | |
| 8 | #include <properties.h> |
| 9 | #define NEED_PROTOTYPES |
| 10 | #include <thing/properties.h> |
| 11 | #include <container/vitems.h> |
| 12 | |
| 13 | // Werte im Mapping in P_VITEMS: |
| 14 | #define VI_PATH 0 // Pfad zur BP |
| 15 | #define VI_REFRESH 1 // Refresheinstellung, s. AddItem |
| 16 | #define VI_OBJECT 2 // Aktuelles Objekt des VItem |
| 17 | #define VI_PROPS 3 // Props fuer das echte Objekt |
| 18 | #define VI_SHADOW_PROPS 4 // Props fuer den shadow/vitem_proxy |
| 19 | #define VI_LAST_OBJ 5 // letztes mitgenommenes Objekt |
| 20 | #define VI_LENGTH 6 |
| 21 | |
| 22 | // VI_REFRESH hat aehnliche Bedeutung wie bei AddItem, mit einer Besonderheit: |
| 23 | // intern wird in Form eines negativen Wertes kodiert, ob das VItem gerade neu |
| 24 | // erzeugt werden darf (negativ: ja, positiv: nein). |
| 25 | |
| 26 | /* Mehr zu VI_REFRESH: |
| 27 | * vitems raeumen sich ggf. selber weg. D.h. wenn der Clone des vitems nicht |
| 28 | * mehr ist, heisst das nicht, dass es jemand mitgenommen hat. In dem Fall |
| 29 | * muss es sofort (wenn seine Praesenz abgefragt wird) neu erzeugt werden. |
| 30 | * Die Logik ist wie folgt: |
| 31 | * Der Standardzustand von VI_REFRESH ist ein negierter Wert. Solange dies der |
| 32 | * Fall ist, wird ein vitem *immer* erzeugt, sofern es abgefragt wird, aber |
| 33 | * nicht da ist. |
| 34 | * Wird ein vitem hingegen bewegt/mitgenommen, wird VI_REFRESH auf den |
| 35 | * positiven Wert geaendert. In diesem Fall wird es NICHT mehr neu erzeugt, |
| 36 | * wenn es nicht als vitem praesent ist. Erst im naechsten reset() wird |
| 37 | * geprueft, ob es in Zukunft wieder neu erzeugt werden darf. Wenn ja, wird |
| 38 | * der Zahlenwert wieder negiert. |
| 39 | * Es ist Absicht, dass VI_REFRESH_ALWAYS auch nur maximal einmal pro reset() |
| 40 | * *mitgenommen* werden darf. |
| 41 | */ |
| 42 | |
| 43 | |
| 44 | //protected void create() |
| 45 | //{ |
| 46 | //} |
| 47 | |
Zesstra | 7390fa9 | 2019-11-30 13:36:52 +0100 | [diff] [blame] | 48 | protected void create_super() { |
| 49 | set_next_reset(-1); |
| 50 | } |
| 51 | |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 52 | public varargs void RemoveVItem(string key) |
| 53 | { |
| 54 | mixed vitems=QueryProp(P_VITEMS); |
| 55 | if (mappingp(vitems) && member(vitems, key)) |
| 56 | { |
| 57 | // Es wird auch zerstoert, wenn das genommene Objekt gerade im Raum |
| 58 | // rumliegt (weil Spieler es hat fallen lassen etc.) |
| 59 | if (vitems[key, VI_OBJECT]) |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 60 | ({int})vitems[key, VI_OBJECT]->remove(1); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 61 | if (vitems[key, VI_LAST_OBJ] |
| 62 | && environment(vitems[key, VI_LAST_OBJ]) == this_object()) |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 63 | ({int})vitems[key, VI_LAST_OBJ]->remove(1); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 64 | |
| 65 | m_delete(vitems, key); |
| 66 | SetProp(P_VITEMS, vitems); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | // TODO: braucht es dynamische refresh, shadowprops und props? |
| 71 | public varargs void AddVItem(string key, int refresh, mapping shadowprops, |
| 72 | string path, mapping props) |
| 73 | { |
| 74 | if (!sizeof(key)) |
| 75 | return; |
| 76 | // Wenn path gegeben, muss es eine ladbare Blueprint sind |
| 77 | if (sizeof(path) && !load_object(path)) |
| 78 | return; |
| 79 | if (!sizeof(path)) |
| 80 | { |
| 81 | if (mappingp(props)) |
| 82 | raise_error("Reine vitems erlauben keine <props>\n"); |
| 83 | } |
| 84 | |
| 85 | refresh ||= VI_REFRESH_NONE; |
| 86 | shadowprops ||= ([]); |
| 87 | // Wenn reines vItem und keine IDs gesetzt, wird <key> als ID verwendet, |
| 88 | // irgendwie muss es ja ansprechbar sein. (Wenn es ein Objekt mit Templat |
| 89 | // ist, hat es normalerweise die IDs aus dem Templat. Wenn man das nicht |
| 90 | // will, muss man es mit gezielter Angabe von P_IDS in den Shadowprops |
| 91 | // ueberschreiben.) Gleiches fuer P_NAME (ohne ist ein "Ding") und P_SHORT |
| 92 | // (ohne P_SHORT ist es unsichtbar) |
| 93 | if (!path) |
| 94 | { |
| 95 | if (!member(shadowprops, P_IDS)) |
| 96 | shadowprops[P_IDS] = ({key}); |
| 97 | if (!member(shadowprops, P_NAME)) |
| 98 | shadowprops[P_NAME] = capitalize(key); |
| 99 | if (!member(shadowprops, P_SHORT)) |
| 100 | shadowprops[P_SHORT] = capitalize(key); |
| 101 | } |
| 102 | mixed vitems=QueryProp(P_VITEMS); |
| 103 | if (!mappingp(vitems)) |
| 104 | vitems = m_allocate(1, VI_LENGTH); |
| 105 | vitems[key, VI_PATH] = path; |
| 106 | vitems[key, VI_REFRESH] = negate(refresh); |
| 107 | vitems[key, VI_PROPS] = props; |
| 108 | vitems[key, VI_SHADOW_PROPS] = shadowprops; |
| 109 | SetProp(P_VITEMS, vitems); |
| 110 | } |
| 111 | |
| 112 | |
| 113 | private void configure_object(object ob, mapping props) |
| 114 | { |
| 115 | foreach (string k, mixed v : props) |
| 116 | { |
| 117 | int reset_prop; |
| 118 | if (k[0] == VI_RESET_PREFIX) |
| 119 | { |
| 120 | reset_prop=1; |
| 121 | k=k[1..]; |
| 122 | } |
| 123 | switch(k) |
| 124 | { |
| 125 | case P_READ_DETAILS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 126 | if (reset_prop) ({void})ob->RemoveReadDetail(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 127 | walk_mapping(v, "AddReadDetail", ob); |
| 128 | break; |
| 129 | case P_DETAILS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 130 | if (reset_prop) ({void})ob->RemoveDetail(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 131 | walk_mapping(v, "AddDetail", ob); |
| 132 | break; |
| 133 | case P_SMELLS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 134 | if (reset_prop) ({void})ob->RemoveSmells(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 135 | walk_mapping(v, "AddSmells", ob); |
| 136 | break; |
| 137 | case P_SOUNDS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 138 | if (reset_prop) ({void})ob->RemoveSounds(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 139 | walk_mapping(v, "AddSounds", ob); |
| 140 | break; |
| 141 | case P_TOUCH_DETAILS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 142 | if (reset_prop) ({void})ob->RemoveTouchDetail(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 143 | walk_mapping(v, "AddTouchDetail", ob); |
| 144 | break; |
| 145 | case P_IDS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 146 | if (reset_prop) ({string*})ob->SetProp(P_IDS, v); |
| 147 | else ({void})ob->AddId(v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 148 | case P_CLASS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 149 | if (reset_prop) ({string*})ob->SetProp(P_CLASS, v); |
| 150 | else ({void})ob->AddClass(v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 151 | case P_ADJECTIVES: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 152 | if (reset_prop) ({string*})ob->SetProp(P_ADJECTIVES, v); |
| 153 | else ({void})ob->AddAdjective(v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 154 | break; |
| 155 | |
| 156 | // Alle anderen Properties stumpf setzen. |
| 157 | default: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 158 | ({mixed})ob->SetProp(k, v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | // Clont ein vitem, falls noetig. |
| 164 | // Nebeneffekt ist aber in jedem Fall auch, dass ein nicht mehr virtuell |
| 165 | // anwesendes Objekt als VI_LAST_OBJ gespeichert und VI_OBJECT geloescht wird. |
| 166 | private void check_vitem(string key, string path, int refresh, |
| 167 | object obj, mapping props, mapping shadow_props, |
| 168 | object last_obj) |
| 169 | { |
| 170 | // Ist es noch da? Ein vItem ist "da", wenn obj auf ein gueltiges Objekt |
| 171 | // zeigt, welches *KEIN* Environment hat. (Hat es ein Environment, wurde es |
| 172 | // mitgenommen.) |
| 173 | if (obj) |
| 174 | { |
| 175 | if (!environment(obj)) |
| 176 | return; |
| 177 | // wenn es mitgenommen wurde, ist obj in jedem Fall kein vItem mehr: |
| 178 | // (eigentlich wird das in VItemMoved schon gemacht, aber mal fuer den |
| 179 | // Fall, dass jemand den Shadow hart entsorgt hat o.ae.) |
| 180 | last_obj = obj; |
| 181 | obj = 0; |
| 182 | // und wird ggf. unten neu erzeugt. |
| 183 | } |
| 184 | |
| 185 | if (refresh < 0) |
| 186 | { |
| 187 | object sh; |
| 188 | if (path) |
| 189 | { |
| 190 | obj=clone_object(path); |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 191 | ({int})obj->SetAutoObject(1); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 192 | if (mappingp(props)) |
| 193 | configure_object(obj, props); |
| 194 | // Schatten erzeugen, welcher die Beschreibung des Objekts im Container nach |
| 195 | // den Props in shadow_props abaendert. |
| 196 | sh = clone_object("/obj/vitem_shadow"); |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 197 | ({void})sh->set_shadow(obj, shadow_props); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 198 | } |
| 199 | else |
| 200 | { |
| 201 | obj=clone_object("/obj/vitem_proxy"); |
| 202 | configure_object(obj, shadow_props); |
| 203 | // no shadow needed in this case. |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | // Erzeugt Instanzen der vItems (sofern noetig und erlaubt durch |
| 209 | // Refresh-Parameter). |
| 210 | private mixed CheckVItems() |
| 211 | { |
| 212 | mixed vitems=QueryProp(P_VITEMS); |
| 213 | if (!mappingp(vitems)) |
| 214 | vitems = m_allocate(0, VI_LENGTH); |
| 215 | walk_mapping(vitems, #'check_vitem); |
| 216 | return vitems; |
| 217 | } |
| 218 | |
| 219 | // Liefert alle in diesem Raum virtuell anwesenden Items |
| 220 | public object *GetVItemClones() |
| 221 | { |
| 222 | mapping vitems = CheckVItems(); |
| 223 | return filter(m_values(vitems, VI_OBJECT), #'objectp); |
| 224 | } |
| 225 | |
| 226 | public object present_vitem(string complex_desc) |
| 227 | { |
| 228 | foreach(object o : GetVItemClones()) |
| 229 | { |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 230 | if (({int})o->id(complex_desc)) |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 231 | return o; |
| 232 | } |
| 233 | return 0; |
| 234 | } |
| 235 | |
| 236 | // wird aus dem Shadow fuer das VItem gerufen, wenn es genomment etc. wird. |
| 237 | // In dem Fall wird das Refresh "gesperrt", d.h. es wird fruehestens nach dem |
| 238 | // naechsten Reset wieder neu erzeugt - sofern im naechsten Reset die |
| 239 | // Vorraussetzungen erfuellt sind. |
| 240 | public void VItemMoved(object ob) |
| 241 | { |
| 242 | if (load_name(previous_object()) == "/obj/vitem_shadow") |
| 243 | { |
| 244 | mapping vitems = QueryProp(P_VITEMS); |
| 245 | if (!mappingp(vitems)) |
| 246 | return; |
| 247 | // passendes vitem suchen |
| 248 | foreach(string key, string path, int refresh, object o, mapping props, |
| 249 | mapping shadow_props, object last_obj: &vitems) |
| 250 | { |
| 251 | if (ob != o) |
| 252 | continue; |
| 253 | else |
| 254 | { |
| 255 | // mitgenommenes Objekt merken und vitem-objekt loeschen |
| 256 | last_obj = o; |
| 257 | o = 0; |
| 258 | // Sperren gegen sofortiges Neuerzeugen, wenn refresh nicht |
| 259 | // VI_REFRESH_INSTANT ist. |
| 260 | if (refresh != VI_REFRESH_INSTANT) |
| 261 | refresh = negate(refresh); |
| 262 | break; |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | void reset() |
| 269 | { |
| 270 | mapping vitems=QueryProp(P_VITEMS); |
| 271 | if (!mappingp(vitems)) |
| 272 | return; |
| 273 | foreach(string key, string path, int refresh, object obj, |
| 274 | mapping props, mapping shadow_props, object last_obj : &vitems) |
| 275 | { |
| 276 | // Wenn negativ (d.h. vItem wurde noch nicht mitgenommen), 0 oder |
| 277 | // REFRESH_NONE, muss hier nix gemacht werden. |
| 278 | if (refresh <= REFRESH_NONE) |
| 279 | continue; |
| 280 | |
| 281 | // Wenn last_obj nicht mehr existiert, darf (ausgenommen natuerlich |
| 282 | // REFFRESH_NONE) immer neu erzeugt werden. |
| 283 | if (!last_obj) |
| 284 | { |
| 285 | refresh=negate(refresh); |
| 286 | continue; |
| 287 | } |
| 288 | |
| 289 | // restliche Faelle |
| 290 | switch(refresh) |
| 291 | { |
| 292 | case VI_REFRESH_MOVE_HOME: |
| 293 | // Wenn das Objekt nicht mehr als vItem hier ist (auch wenn es hier im |
| 294 | // Raum liegt!), wird es heim bewegt (sprich: zerstoert und neues |
| 295 | // vitem). |
| 296 | // (Da man hier nur hinkommt, wenn es mitgenommen wurde (refresh > 0), |
| 297 | // wird es immer refresht... |
| 298 | // Zu beachten: es soll auch nicht hier in diesem Container rumliegen |
| 299 | // nach dem Heimbewegen, also zerstoeren! |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 300 | ({int})last_obj->remove(1); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 301 | // Fallthrough |
| 302 | case VI_REFRESH_REMOVE: |
| 303 | // wenn nicht mehr als vItem hier ist (d.h. auch wenn es hier im Raum |
| 304 | // rumliegt!) darf es neu erzeugt werden. |
| 305 | // (Hierher kommt die Ausfuehrung nur her, wenn es mitgenommen |
| 306 | // wurde, d.h. letztendlich: immer. d.h. Fallthrough.) |
| 307 | case VI_REFRESH_ALWAYS: |
| 308 | // neu erzeugen |
| 309 | refresh=negate(refresh); |
| 310 | break; |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | |