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 |
Zesstra | a724a84 | 2020-06-13 20:24:59 +0200 | [diff] [blame] | 91 | // ueberschreiben.) Gleiches fuer P_NAME (ohne ist ein "Ding") |
Zesstra | 4a59a29 | 2020-06-18 22:42:43 +0200 | [diff] [blame] | 92 | // P_SHORT wird *nicht* automatisch gesetzt und sogar gezielt genullt. Ohne |
| 93 | // Kurzbeschreibung ist es zwar nicht wahrnehmbar, aber untersuchbar und der |
| 94 | // Zustand ist gar nicht selten gewuenscht fuer vitems. |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 95 | if (!path) |
| 96 | { |
| 97 | if (!member(shadowprops, P_IDS)) |
| 98 | shadowprops[P_IDS] = ({key}); |
| 99 | if (!member(shadowprops, P_NAME)) |
| 100 | shadowprops[P_NAME] = capitalize(key); |
Zesstra | 4a59a29 | 2020-06-18 22:42:43 +0200 | [diff] [blame] | 101 | if (!member(shadowprops, P_SHORT)) |
| 102 | shadowprops[P_SHORT] = 0; |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 103 | } |
| 104 | mixed vitems=QueryProp(P_VITEMS); |
| 105 | if (!mappingp(vitems)) |
| 106 | vitems = m_allocate(1, VI_LENGTH); |
| 107 | vitems[key, VI_PATH] = path; |
| 108 | vitems[key, VI_REFRESH] = negate(refresh); |
| 109 | vitems[key, VI_PROPS] = props; |
| 110 | vitems[key, VI_SHADOW_PROPS] = shadowprops; |
| 111 | SetProp(P_VITEMS, vitems); |
| 112 | } |
| 113 | |
| 114 | |
| 115 | private void configure_object(object ob, mapping props) |
| 116 | { |
| 117 | foreach (string k, mixed v : props) |
| 118 | { |
| 119 | int reset_prop; |
| 120 | if (k[0] == VI_RESET_PREFIX) |
| 121 | { |
| 122 | reset_prop=1; |
| 123 | k=k[1..]; |
| 124 | } |
| 125 | switch(k) |
| 126 | { |
| 127 | case P_READ_DETAILS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 128 | if (reset_prop) ({void})ob->RemoveReadDetail(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 129 | walk_mapping(v, "AddReadDetail", ob); |
| 130 | break; |
| 131 | case P_DETAILS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 132 | if (reset_prop) ({void})ob->RemoveDetail(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 133 | walk_mapping(v, "AddDetail", ob); |
| 134 | break; |
| 135 | case P_SMELLS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 136 | if (reset_prop) ({void})ob->RemoveSmells(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 137 | walk_mapping(v, "AddSmells", ob); |
| 138 | break; |
| 139 | case P_SOUNDS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 140 | if (reset_prop) ({void})ob->RemoveSounds(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 141 | walk_mapping(v, "AddSounds", ob); |
| 142 | break; |
| 143 | case P_TOUCH_DETAILS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 144 | if (reset_prop) ({void})ob->RemoveTouchDetail(0); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 145 | walk_mapping(v, "AddTouchDetail", ob); |
| 146 | break; |
| 147 | case P_IDS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 148 | if (reset_prop) ({string*})ob->SetProp(P_IDS, v); |
| 149 | else ({void})ob->AddId(v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 150 | case P_CLASS: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 151 | if (reset_prop) ({string*})ob->SetProp(P_CLASS, v); |
| 152 | else ({void})ob->AddClass(v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 153 | case P_ADJECTIVES: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 154 | if (reset_prop) ({string*})ob->SetProp(P_ADJECTIVES, v); |
| 155 | else ({void})ob->AddAdjective(v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 156 | break; |
| 157 | |
| 158 | // Alle anderen Properties stumpf setzen. |
| 159 | default: |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 160 | ({mixed})ob->SetProp(k, v); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | // Clont ein vitem, falls noetig. |
| 166 | // Nebeneffekt ist aber in jedem Fall auch, dass ein nicht mehr virtuell |
| 167 | // anwesendes Objekt als VI_LAST_OBJ gespeichert und VI_OBJECT geloescht wird. |
| 168 | private void check_vitem(string key, string path, int refresh, |
| 169 | object obj, mapping props, mapping shadow_props, |
| 170 | object last_obj) |
| 171 | { |
| 172 | // Ist es noch da? Ein vItem ist "da", wenn obj auf ein gueltiges Objekt |
| 173 | // zeigt, welches *KEIN* Environment hat. (Hat es ein Environment, wurde es |
| 174 | // mitgenommen.) |
| 175 | if (obj) |
| 176 | { |
| 177 | if (!environment(obj)) |
| 178 | return; |
| 179 | // wenn es mitgenommen wurde, ist obj in jedem Fall kein vItem mehr: |
| 180 | // (eigentlich wird das in VItemMoved schon gemacht, aber mal fuer den |
| 181 | // Fall, dass jemand den Shadow hart entsorgt hat o.ae.) |
| 182 | last_obj = obj; |
| 183 | obj = 0; |
| 184 | // und wird ggf. unten neu erzeugt. |
| 185 | } |
| 186 | |
| 187 | if (refresh < 0) |
| 188 | { |
| 189 | object sh; |
| 190 | if (path) |
| 191 | { |
| 192 | obj=clone_object(path); |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 193 | ({int})obj->SetAutoObject(1); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 194 | if (mappingp(props)) |
| 195 | configure_object(obj, props); |
| 196 | // Schatten erzeugen, welcher die Beschreibung des Objekts im Container nach |
| 197 | // den Props in shadow_props abaendert. |
| 198 | sh = clone_object("/obj/vitem_shadow"); |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 199 | ({void})sh->set_shadow(obj, shadow_props); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 200 | } |
| 201 | else |
| 202 | { |
| 203 | obj=clone_object("/obj/vitem_proxy"); |
| 204 | configure_object(obj, shadow_props); |
| 205 | // no shadow needed in this case. |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | // Erzeugt Instanzen der vItems (sofern noetig und erlaubt durch |
| 211 | // Refresh-Parameter). |
| 212 | private mixed CheckVItems() |
| 213 | { |
| 214 | mixed vitems=QueryProp(P_VITEMS); |
| 215 | if (!mappingp(vitems)) |
| 216 | vitems = m_allocate(0, VI_LENGTH); |
| 217 | walk_mapping(vitems, #'check_vitem); |
| 218 | return vitems; |
| 219 | } |
| 220 | |
| 221 | // Liefert alle in diesem Raum virtuell anwesenden Items |
| 222 | public object *GetVItemClones() |
| 223 | { |
| 224 | mapping vitems = CheckVItems(); |
| 225 | return filter(m_values(vitems, VI_OBJECT), #'objectp); |
| 226 | } |
| 227 | |
| 228 | public object present_vitem(string complex_desc) |
| 229 | { |
| 230 | foreach(object o : GetVItemClones()) |
| 231 | { |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 232 | if (({int})o->id(complex_desc)) |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 233 | return o; |
| 234 | } |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | // wird aus dem Shadow fuer das VItem gerufen, wenn es genomment etc. wird. |
| 239 | // In dem Fall wird das Refresh "gesperrt", d.h. es wird fruehestens nach dem |
| 240 | // naechsten Reset wieder neu erzeugt - sofern im naechsten Reset die |
| 241 | // Vorraussetzungen erfuellt sind. |
| 242 | public void VItemMoved(object ob) |
| 243 | { |
| 244 | if (load_name(previous_object()) == "/obj/vitem_shadow") |
| 245 | { |
| 246 | mapping vitems = QueryProp(P_VITEMS); |
| 247 | if (!mappingp(vitems)) |
| 248 | return; |
| 249 | // passendes vitem suchen |
| 250 | foreach(string key, string path, int refresh, object o, mapping props, |
| 251 | mapping shadow_props, object last_obj: &vitems) |
| 252 | { |
| 253 | if (ob != o) |
| 254 | continue; |
| 255 | else |
| 256 | { |
| 257 | // mitgenommenes Objekt merken und vitem-objekt loeschen |
| 258 | last_obj = o; |
| 259 | o = 0; |
| 260 | // Sperren gegen sofortiges Neuerzeugen, wenn refresh nicht |
| 261 | // VI_REFRESH_INSTANT ist. |
| 262 | if (refresh != VI_REFRESH_INSTANT) |
| 263 | refresh = negate(refresh); |
| 264 | break; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | void reset() |
| 271 | { |
| 272 | mapping vitems=QueryProp(P_VITEMS); |
| 273 | if (!mappingp(vitems)) |
| 274 | return; |
| 275 | foreach(string key, string path, int refresh, object obj, |
| 276 | mapping props, mapping shadow_props, object last_obj : &vitems) |
| 277 | { |
| 278 | // Wenn negativ (d.h. vItem wurde noch nicht mitgenommen), 0 oder |
| 279 | // REFRESH_NONE, muss hier nix gemacht werden. |
| 280 | if (refresh <= REFRESH_NONE) |
| 281 | continue; |
| 282 | |
| 283 | // Wenn last_obj nicht mehr existiert, darf (ausgenommen natuerlich |
| 284 | // REFFRESH_NONE) immer neu erzeugt werden. |
| 285 | if (!last_obj) |
| 286 | { |
| 287 | refresh=negate(refresh); |
| 288 | continue; |
| 289 | } |
| 290 | |
| 291 | // restliche Faelle |
| 292 | switch(refresh) |
| 293 | { |
| 294 | case VI_REFRESH_MOVE_HOME: |
| 295 | // Wenn das Objekt nicht mehr als vItem hier ist (auch wenn es hier im |
| 296 | // Raum liegt!), wird es heim bewegt (sprich: zerstoert und neues |
| 297 | // vitem). |
| 298 | // (Da man hier nur hinkommt, wenn es mitgenommen wurde (refresh > 0), |
| 299 | // wird es immer refresht... |
| 300 | // Zu beachten: es soll auch nicht hier in diesem Container rumliegen |
| 301 | // nach dem Heimbewegen, also zerstoeren! |
bugfix | d94d093 | 2020-04-08 11:27:13 +0200 | [diff] [blame] | 302 | ({int})last_obj->remove(1); |
Zesstra | 4403045 | 2018-11-12 22:34:02 +0100 | [diff] [blame] | 303 | // Fallthrough |
| 304 | case VI_REFRESH_REMOVE: |
| 305 | // wenn nicht mehr als vItem hier ist (d.h. auch wenn es hier im Raum |
| 306 | // rumliegt!) darf es neu erzeugt werden. |
| 307 | // (Hierher kommt die Ausfuehrung nur her, wenn es mitgenommen |
| 308 | // wurde, d.h. letztendlich: immer. d.h. Fallthrough.) |
| 309 | case VI_REFRESH_ALWAYS: |
| 310 | // neu erzeugen |
| 311 | refresh=negate(refresh); |
| 312 | break; |
| 313 | } |
| 314 | } |
| 315 | } |
| 316 | |