‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
_zm_craftables.gsc
Go to the documentation of this file.
1 #using scripts\codescripts\struct;
2 
3 #using scripts\shared\array_shared;
4 #using scripts\shared\callbacks_shared;
5 #using scripts\shared\clientfield_shared;
6 #using scripts\shared\demo_shared;
7 #using scripts\shared\flag_shared;
8 #using scripts\shared\hud_util_shared;
9 #using scripts\shared\laststand_shared;
10 #using scripts\shared\system_shared;
11 #using scripts\shared\util_shared;
12 
13 #insert scripts\shared\shared.gsh;
14 #insert scripts\shared\version.gsh;
15 
16 #using scripts\zm\_zm_bgb;
17 #using scripts\zm\_zm_equipment;
18 #using scripts\zm\_zm_stats;
19 #using scripts\zm\_zm_unitrigger;
20 #using scripts\zm\_zm_utility;
21 #using scripts\zm\_zm_weapons;
22 
23 #insert scripts\zm\_zm_perks.gsh;
24 #insert scripts\zm\_zm_utility.gsh;
25 #insert scripts\zm\craftables\_zm_craftables.gsh;
26 
27 
28 //
29 // A rundown of how the data is structured:
30 // UTS - unitrigger stub with craftable info
31 // PSt = Piece Stub - (piece_header data)
32 // PSp = Piece Spawn - (piece_instance data)
33 // CSt = Craftable Stub - header info on craftables
34 // CSp = Craftable Spawn...(craftable_instance)
35 // These structs contain duplicated data and also refer to each other. It's a mess.
36 // Entries with the same indicator means they are the same data struct.
37 //
38 //CSt level.zombie_include_craftables contains an array of the basic craftable data
39 //PSt .a_pieceStubs
40 // (hint strings)
41 // .name
42 // .onFullyCrafted - callback for when all pieces have been crafted
43 // .triggerThink
44 //
45 //
46 // level.zombie_craftableStubs - same as level.zombie_include_craftables... why!?!?
47 //
48 //
49 // UTS level.a_uts_craftables contains an array of unitrigger stubs, which is crafting (table) trigger info
50 // (trigger info)
51 // CSt .craftableStub - general crafting info
52 // (hint strings)
53 // PSt .a_pieceStubs - array of general piece data (copied to the specific piece in the world)
54 // (piece info - offsets, callback functions)
55 // .client_field_id - clientfield name to set when a shared item is picked up (undefined if not shared)
56 // .client_field_state - state to set when an non-shared item is picked up (undefined if shared)
57 // .craftablename - name of parent craftable
58 // PSp .pieceSpawn - piece in the world
59 // .is_shared - is this a shared item (global inventory)
60 // .need_all_pieces - must have all pieces before you can start building (undefined otherwise)
61 // .name - of craftable
62 // (buy and trigger think function pointers)
63 // CSp .craftableSpawn - actual in-world data
64 // .craftable_name - name of craftable
65 // PSp .a_pieceSpawns - array of specific pieces in the world
66 // (piece info - offsets, callback functions)
67 // .craftablename - name of craftable
68 // .crafted - defined to 1 after piece is placed
69 // .in_shared_inventory - a shared item that was picked up.
70 // .is_shared - is this a shared item (global inventory)
71 // Pst .pieceStub - points to struct of general piece data
72 // UTS .stub - (the parent stub in level.a_uts_craftables), also, .stub value in a unitrigger ent.
73 // .crafted - was this object completely crafted?
74 // .equipname - craftable name
75 // .model - the script_model pointed to by the trigger (if any).
76 // .targetname - name of the trigger
77 //
78 
79 #precache( "string", "ZOMBIE_BUILDING" );
80 #precache( "string", "ZOMBIE_BUILD_PIECE_MISSING" );
81 #precache( "string", "ZOMBIE_BUILD_PIECE_GRAB" );
82 #precache( "triggerstring", "ZOMBIE_BUILD_PIECE_MORE" );
83 #precache( "triggerstring", "ZOMBIE_BUILD_PIECE_GRAB" );
84 #precache( "triggerstring", "ZOMBIE_CRAFTABLE_NO_SWITCH" );
85 #precache( "triggerstring", "ZOMBIE_BUILD_PIECE_SWITCH" );
86 #precache( "triggerstring", "ZOMBIE_BUILD_PIECE_WRONG" );
87 #precache( "triggerstring", "ZOMBIE_BUILD_PIECE_ONLY_ONE" );
88 #precache( "triggerstring", "ZOMBIE_BUILD_PIECE_HAVE_ONE" );
89 #precache( "triggerstring", "ZOMBIE_GO_TO_THE_BOX_LIMITED" );
90 #precache( "triggerstring", "ZOMBIE_GO_TO_THE_BOX" );
91 #precache( "triggerstring", "ZOMBIE_CRAFTABLE_CHANGE_BUILD" );
92 
93 #precache( "fx", "zombie/fx_crafting_dust_zmb" );
94 
95 #define ZM_CRAFTABLES_LOOKAT_DOT 0.76
96 #define ZM_CRAFTABLES_NOT_ENOUGH_PIECES_UI_DURATION 3.5
97 #define ZM_CRAFTABLES_FULLY_CRAFTED_UI_DURATION 3.5
98 
99 #namespace zm_craftables;
100 
101 ‪REGISTER_SYSTEM_EX( "zm_craftables", &‪__init__, &‪__main__, undefined )
102 
103 function ‪__init__()
104 {
106 }
107 
108 function ‪init()
109 {
110  // Allow Players To Swap Carry Pieces
111  //-----------------------------------
112  ‪DEFAULT( level.craftable_piece_swap_allowed, true );
113 
114  zombie_craftables_callbacks = [];
115 
116  level.craftablePickUps = [];
117  level.craftables_crafted = [];
118  level.a_uts_craftables = [];
119 
120  if ( !isdefined( level.craftable_piece_count ) )
121  {
122  level.craftable_piece_count = 0;
123  }
124 
125  level._effect["building_dust"] = "zombie/fx_crafting_dust_zmb";
126 
127  if ( isdefined( level.init_craftables ) )
128  {
129  [[level.init_craftables]]();
130  }
131 
132  // create the stub for the open craftable table - any open-type craftable can be made at these places
133  open_table = SpawnStruct();
134  open_table.name = "open_table";
135  open_table.triggerThink = &‪openTableCraftable;
136  open_table.custom_craftablestub_update_prompt = &‪open_craftablestub_update_prompt;
137  ‪include_zombie_craftable( open_table );
138  ‪add_zombie_craftable( "open_table", &"" );
139 
140  // DT 101314 Zombies have difficulty attacking players that are clipping into large craftable pieces
141  if ( isdefined( level.use_swipe_protection ) )
142  {
144  }
145 }
146 
147 function ‪__main__()
148 {
149  level thread ‪think_craftables();
150 }
151 
153 {
154  ‪set_piece_count( level.zombie_craftableStubs.size );
155 }
156 
157 function ‪anystub_update_prompt( player )
158 {
159  //no craftable trigger can be used while in last stand or if you could be reviving someone
161  {
162  self.hint_string = "";
163  return false;
164  }
165 
166 // if( player isThrowingGrenade() )
167 // {
168 // self.hint_string = "";
169 // return false;
170 // }
171  if ( isdefined( player.is_drinking ) && ‪IS_DRINKING( player.is_drinking ) )
172  {
173  self.hint_string = "";
174  return false;
175  }
176 
177  if ( isdefined( player.screecher_weapon ) )
178  {
179  self.hint_string = "";
180  return false;
181  }
182 
183  initial_current_weapon = player getCurrentWeapon();
184  current_weapon = ‪zm_weapons::get_nonalternate_weapon( initial_current_weapon );
185  if ( ‪zm_equipment::is_equipment( current_weapon ) )
186  {
187  self.hint_string = "";
188  return false;
189  }
190 
191  return true;
192 }
193 
195 {
196  if ( isdefined( self.origin_parent ) )
197  {
198  return self.origin_parent.origin;
199  }
200 
201  return self.origin;
202 }
203 
204 function ‪anystub_on_spawn_trigger( trigger )
205 {
206  if ( isdefined( self.link_parent ) )
207  {
208  trigger EnableLinkTo();
209  trigger LinkTo( self.link_parent );
210  trigger SetMovingPlatformEnabled( true );
211  }
212 }
213 
215 {
216  self endon( "disconnect" );
217  self notify( "craftables_watch_swipes" );
218  self endon( "craftables_watch_swipes" );
219 
220  while ( 1 )
221  {
222  self waittill( "melee_swipe", zombie );
223 
224  if ( distancesquared( zombie.origin, self.origin ) > zombie.meleeattackdist * zombie.meleeattackdist )
225  {
226  continue; // just flailing at nothing
227  }
228 
229  trigger = level._unitriggers.trigger_pool[self GetEntityNumber()];
230 
231  if ( isdefined( trigger ) && isdefined( trigger.stub.piece ) )
232  {
233  piece = trigger.stub.piece;
234 
235  if ( !isdefined( piece.damage ) )
236  {
237  piece.damage = 0;
238  }
239 
240  piece.damage++;
241 
242  if ( piece.damage > 12 )
243  {
245  piece ‪piece_unspawn();
246  self ‪zm_stats::increment_client_stat( "cheat_total", false );
247 
248  if ( IsAlive( self ) )
249  {
250  self playlocalsound( level.zmb_laugh_alias );
251  }
252  }
253  }
254  }
255 }
256 
257 
259 {
260  self DoDamage( ‪damage, pos );
261 }
262 
263 
264 // Makes a craftable able to be crafted at any table. Extra info is required, such as a localized name string
265 // and model information for the craftable (we need to be able to spawn this at any open craftable table).
266 // Params:
267 // str_craftable - the name of the craftable to modify
268 // str_model - model name of the fully crafted item
269 // v_angle_offset - angles to add to the model when it's spawned (to adjust its position on the table)
270 // v_origin_offset - position to add to the model when it's spawned (to adjust its position on the table)
271 function ‪make_zombie_craftable_open( str_craftable, str_model, v_angle_offset, v_origin_offset )
272 {
273  Assert( isdefined( level.zombie_craftableStubs[ str_craftable ] ), "Craftable " + str_craftable + " has not been added yet." );
274 
275  //PrecacheModel( str_model );
276 
277  s_craftable = level.zombie_craftableStubs[ str_craftable ];
278  s_craftable.is_open_table = true;
279  s_craftable.str_model = str_model;
280  s_craftable.v_angle_offset = v_angle_offset;
281  s_craftable.v_origin_offset = v_origin_offset;
282 }
283 
284 
285 
286 //
287 // add general info about a craftable.
288 // craftable_name - ID
289 // str_to_craft - Hold X to craft Y hint string
290 // str_crafting - Part added hint string (not neeeded if NEED_ALL_PIECES)
291 // str_taken - Player obtained craftable from table hint string (PERSISTENT and ONE_USE_AND_FLY)
292 // need_all_pieces - are all pieces required before you can craft the object?
293 // onFullyCrafted - callback when the item is fully crafted. Blocking call.
294 function ‪add_zombie_craftable( craftable_name, str_to_craft, str_crafting, str_taken, onFullyCrafted, need_all_pieces )
295 {
296  if ( !isdefined( level.zombie_include_craftables ) )
297  {
298  level.zombie_include_craftables = [];
299  }
300 
301  if ( isdefined( level.zombie_include_craftables ) && !isdefined( level.zombie_include_craftables[ craftable_name ] ) )
302  {
303  return;
304  }
305 
306  // This can be undefined if it uses the system, but not for an actual crafted item.
307  if ( isdefined( str_to_craft ) )
308  {
309  //PrecacheString( str_to_craft );
310  }
311 
312  // Not needed if it's a shared item that's used in another manner
313  if ( isdefined( str_crafting ) )
314  {
315  //PrecacheString( str_crafting );
316  }
317 
318  //if (isdefined(str_taken))
319  //PrecacheString( str_taken );
320 
321  craftable_struct = level.zombie_include_craftables[ craftable_name ];
322 
323  if ( !isdefined( level.zombie_craftableStubs ) )
324  {
325  level.zombie_craftableStubs = [];
326  }
327 
328  craftable_struct.str_to_craft = str_to_craft;
329  craftable_struct.str_crafting = str_crafting;
330  craftable_struct.str_taken = str_taken;
331  craftable_struct.onFullyCrafted = onFullyCrafted;
332  craftable_struct.need_all_pieces = need_all_pieces;
333 
334  level.zombie_craftableStubs[ craftable_struct.name ] = craftable_struct;
335 }
336 
337 function ‪set_hide_model_if_unavailable( craftable_name, hide_when_unavailable )
338 {
339  if ( isdefined( level.zombie_craftableStubs[ craftable_name ] ) )
340  {
341  level.zombie_craftableStubs[ craftable_name ].hide_when_unavailable = hide_when_unavailable;
342  }
343 }
344 
345 function ‪get_hide_model_if_unavailable( craftable_name )
346 {
347  if ( isdefined( level.zombie_craftableStubs[ craftable_name ] ) )
348  {
349  return ‪IS_TRUE( level.zombie_craftableStubs[ craftable_name ].hide_when_unavailable );
350  }
351  return false;
352 }
353 
354 function ‪set_build_time( craftable_name, build_time )
355 {
356  if ( isdefined( level.zombie_craftableStubs[ craftable_name ] ) )
357  {
358  level.zombie_craftableStubs[ craftable_name ].useTime = build_time;
359  }
360 }
361 
362 function ‪set_piece_count( n_count )
363 {
364  bits = GetMinBitCountForNum( n_count );
365  RegisterClientField( "toplayer", ‪CLIENTFIELD_CRAFTABLE, ‪VERSION_SHIP, bits, "int" );
366 }
367 
368 function ‪add_zombie_craftable_vox_category( craftable_name, vox_id )
369 {
370  craftable_struct = level.zombie_include_craftables[ craftable_name ];
371 
372  craftable_struct.vox_id = vox_id;
373 }
374 
375 function ‪include_zombie_craftable( craftableStub )
376 {
377  if ( !isdefined( level.zombie_include_craftables ) )
378  {
379  level.zombie_include_craftables = [];
380  }
381 
382  if ( !isDefined( level.craftableIndex ) )
383  {
384  level.craftableIndex = 0;
385  }
386 
387  level.zombie_include_craftables[ craftableStub.name ] = craftableStub;
388  craftableStub.hash_id = HashString(craftablestub.name);
389 }
390 
391 
392 //
393 // Sets up a piece of a craftable.
394 //
395 // craftableName - alias name of the craftable
396 // pieceName - alias name of the part, if not specified, it will use the modelName as the pieceName.
397 // modelName - xmodel name
398 // radius - approximate size (used for making the unitrigger)
399 // height - approximate size (used for making the unitrigger)
400 // drop_offset - ground offset if we need to drop the item
401 // hud_icon - need to kill this UNUSED
402 // onPickup - Callback when item picked up
403 // onDrop - Callback when item dropped
404 // onCrafted - Callback when item is crafted (used on the table)
405 // use_spawn_num - set a specific spawner location for respawning
406 // tag_name - model tag to show when crafted
407 // can_reuse - set to true if the same part is used by more than one craftable (no need to create another data struct if so)
408 // client_field_state -
409 // NOTE: for non-shared items, this will represent a clientfield state number that is stored in the CLIENTFIELD_CRAFTABLE ClientField
410 // For Shared items, this will represent the name of the Clientfield to update. This is not the cleanest
411 // implementation, but the two types of pieces are handled differently only in this regard.
412 //
413 // is_shared - indicates if an item is shared inventory amongst all players
414 // vox_id - string to play custom VO lines
415 // hint_string - override the generic piece pick-up string
416 function ‪generate_zombie_craftable_piece( craftablename, pieceName, radius, height, drop_offset, hud_icon, ‪onPickup, ‪onDrop, onCrafted, use_spawn_num, ‪tag_name, can_reuse, client_field_value, is_shared = FALSE, vox_id, b_one_time_vo = false, hint_string, slot = 0 )
417 {
418  pieceStub = spawnstruct();
419 
420  craftable_pieces = [];
421 
422  if ( !isdefined( pieceName ) )
423  {
424  AssertMsg( "You must provide an alias name for this piece" );
425  }
426 
427  craftable_pieces_structs = ‪struct::get_array( craftablename + "_" + pieceName, "targetname" );
428 
429  if (!isDefined(level.craftablePieceIndex))
430  {
431  level.craftablePieceIndex = 0;
432  }
433 
434  foreach ( index, struct in craftable_pieces_structs )
435  {
436  craftable_pieces[ index ] = struct;
437  craftable_pieces[ index ].hasSpawned = false;
438  }
439 
440  pieceStub.spawns = craftable_pieces;
441 
442  pieceStub.craftableName = craftableName;
443  pieceStub.pieceName = pieceName;
444  if ( craftable_pieces.size )
445  {
446  pieceStub.modelName = craftable_pieces[0].model;
447  }
448  pieceStub.hud_icon = hud_icon;
449  pieceStub.radius = radius;
450  pieceStub.height = height;
451  pieceStub.tag_name = ‪tag_name;
452  pieceStub.can_reuse = can_reuse;
453  pieceStub.drop_offset = drop_offset;
454  pieceStub.max_instances = 256;
455 
456  pieceStub.onPickup = ‪onPickup;
457  pieceStub.onDrop = ‪onDrop;
458  pieceStub.onCrafted = onCrafted;
459  pieceStub.use_spawn_num = use_spawn_num;
460  pieceStub.is_shared = is_shared;
461  pieceStub.vox_id = vox_id;
462  pieceStub.hint_string = hint_string;
463  pieceStub.inventory_slot = slot;
464 
465  pieceStub.hash_id = HashString(pieceName);
466 
467  if ( ‪IS_TRUE( b_one_time_vo ) )
468  {
469  pieceStub.b_one_time_vo = b_one_time_vo;
470  }
471 
472  if ( isdefined( client_field_value ) )
473  {
474  if ( ‪IS_TRUE( is_shared ) )
475  {
476  Assert( IsString( client_field_value ), "Client field value for shared item (" + pieceName + ") should be a string (the name of the ClientField to use)" );
477  pieceStub.client_field_id = client_field_value;
478  }
479  else
480  {
481  pieceStub.client_field_state = client_field_value;
482  }
483  }
484 
485  return pieceStub;
486 }
487 
488 function ‪manage_multiple_pieces( max_instances )
489 {
490  self.max_instances = max_instances;
491  self.managing_pieces = true;
492  self.piece_allocated = [];
493 }
494 
495 
496 function ‪combine_craftable_pieces( piece1, piece2, piece3 )
497 {
498  spawns1 = piece1.spawns;
499  spawns2 = piece2.spawns;
500 
501  spawns = ArrayCombine( spawns1, spawns2, true, false );
502 
503  if ( isdefined( piece3 ) )
504  {
505  spawns3 = piece3.spawns;
506  spawns = ArrayCombine( spawns, spawns3, true, false );
507  spawns = array::randomize( spawns );
508 
509  // add piece 4 here if needed
510 
511  piece3.spawns = spawns;
512  }
513  else
514  {
515  spawns = array::randomize( spawns );
516  }
517 
518  piece1.spawns = spawns;
519  piece2.spawns = spawns;
520 }
521 
522 function ‪add_craftable_piece( pieceStub, ‪tag_name, can_reuse )
523 {
524  if ( !isdefined( self.a_pieceStubs ) )
525  {
526  self.a_pieceStubs = [];
527  }
528 
529  if ( isdefined( ‪tag_name ) )
530  {
531  pieceStub.tag_name = ‪tag_name;
532  }
533 
534  if ( isdefined( can_reuse ) )
535  {
536  pieceStub.can_reuse = can_reuse;
537  }
538 
539  self.a_pieceStubs[self.a_pieceStubs.size] = pieceStub;
540  ‪DEFAULT( self.inventory_slot, pieceStub.inventory_slot );
541 }
542 
543 
544 //
545 // This would be better if it was in the _zm_laststand::PlayerLastStand func like the buildables.
546 // However, we're trying not to modify any existing common scripts.
547 // self is a player
549 {
550  self endon( "craftable_piece_released" + slot );
551 
552 // self waittill("entering_last_stand");
553  self waittill( "bled_out" );
554 
556 }
557 
558 
559 // Player last stand function. Drops currently held piece.
560 // self is a player
562 {
563  ‪DEFAULT( self.current_craftable_pieces, [] );
564 
565  foreach ( index, piece in self.current_craftable_pieces )
566  {
567  if ( isdefined( piece ) )
568  {
569  return_to_start_pos = false;
570 
571  if ( isdefined( level.safe_place_for_craftable_piece ) )
572  {
573  if ( ! self [[level.safe_place_for_craftable_piece]]( piece ) )
574  {
575  return_to_start_pos = true;
576  }
577  }
578 
579  if ( return_to_start_pos )
580  {
581  piece ‪piece_spawn_at();
582  }
583  else
584  {
585  piece ‪piece_spawn_at( self.origin + ( 5, 5, 0 ), self.angles );
586  }
587 
588  if ( isdefined( piece.onDrop ) )
589  {
590  piece [[ piece.onDrop ]]( self );
591  }
592 
594  }
595 
596  self.current_craftable_pieces[index] = undefined;
597  self notify( "craftable_piece_released" + index );
598  }
599 }
600 
601 // place the unitrigger a few inches above the origin to avoid LOS issues with the ground
602 #define PIECE_UNITRIGGER_OFFSET (0,0,12)
603 
605 {
606  if ( isdefined( self.origin_parent ) )
607  {
608  return self.origin_parent.origin + ‪PIECE_UNITRIGGER_OFFSET;
609  }
610 
611  return self.origin;
612 }
613 
614 function ‪generate_piece_unitrigger( classname, origin, angles, flags, radius, script_height, hint_string, moving, b_nolook )
615 {
616  if ( !isdefined( radius ) )
617  {
618  radius = 64;
619  }
620 
621  if ( !isdefined( script_height ) )
622  {
623  script_height = 64;
624  }
625 
626  script_width = script_height;
627 
628  if ( !isdefined( script_width ) )
629  {
630  script_width = 64;
631  }
632 
633  script_length = script_height;
634 
635  if ( !isdefined( script_length ) )
636  {
637  script_length = 64;
638  }
639 
640 
641  unitrigger_stub = spawnstruct();
642 
643  unitrigger_stub.origin = origin;
644  //unitrigger_stub.angles = trig.angles;
645 
646 
647  if ( isdefined( script_length ) )
648  {
649  unitrigger_stub.script_length = script_length;
650  }
651  else
652  {
653  unitrigger_stub.script_length = 13.5;
654  }
655 
656  if ( isdefined( script_width ) )
657  {
658  unitrigger_stub.script_width = script_width;
659  }
660  else
661  {
662  unitrigger_stub.script_width = 27.5;
663  }
664 
665  if ( isdefined( script_height ) )
666  {
667  unitrigger_stub.script_height = script_height;
668  }
669  else
670  {
671  unitrigger_stub.script_height = 24;
672  }
673 
674  unitrigger_stub.radius = radius;
675 
676  //unitrigger_stub.target = trig.target;
677  //unitrigger_stub.targetname = trig.targetname;
678 
679  unitrigger_stub.cursor_hint = "HINT_NOICON";
680 
681  if ( isdefined( hint_string ) )
682  {
683  unitrigger_stub.hint_string_override = hint_string; // hint_string_override lets the update function know to use this instead
684  unitrigger_stub.hint_string = unitrigger_stub.hint_string_override;
685  }
686  else
687  {
688  unitrigger_stub.hint_string = &"ZOMBIE_BUILD_PIECE_GRAB";
689  }
690 
691  unitrigger_stub.script_unitrigger_type = "unitrigger_box_use";
692 
693  //TODO: Get a custom KVP for this
694  if (isdefined (b_nolook) && ‪IS_TRUE(Int(b_nolook)))
695  {
696  unitrigger_stub.require_look_toward = false;
697  }
698 
699  unitrigger_stub.require_look_at = false; //true;
700 
701 
702 
703  switch ( classname )
704  {
705  case "trigger_radius":
706  unitrigger_stub.script_unitrigger_type = "unitrigger_radius";
707  break;
708 
709  case "trigger_radius_use":
710  unitrigger_stub.script_unitrigger_type = "unitrigger_radius_use";
711  break;
712 
713  case "trigger_box":
714  unitrigger_stub.script_unitrigger_type = "unitrigger_box";
715  break;
716 
717  case "trigger_box_use":
718  unitrigger_stub.script_unitrigger_type = "unitrigger_box_use";
719  break;
720  }
721 
723  unitrigger_stub.prompt_and_visibility_func = &‪piecetrigger_update_prompt;
724  unitrigger_stub.originFunc = &‪piecestub_get_unitrigger_origin;
725  unitrigger_stub.onSpawnFunc = &‪anystub_on_spawn_trigger;
726 
727  if ( ‪IS_TRUE( moving ) )
728  {
730  }
731  else
732  {
734  }
735 
736  return unitrigger_stub;
737 }
738 
739 
740 // self is a unitrigger
742 {
743  ‪DEFAULT( player.current_craftable_pieces, [] );
744  can_use = self.stub ‪piecestub_update_prompt( player );
745  self setInvisibleToPlayer( player, !can_use );
746  self SetHintString( self.stub.hint_string );
747  return can_use;
748 }
749 
750 
751 //
752 // Update the unitrigger prompt
753 // Returns false if the trigger should not be usable; true if usable
754 // self is a unitrigger stub
755 function ‪piecestub_update_prompt( player, slot = self.piece.inventory_slot )
756 {
757  if ( !self ‪anystub_update_prompt( player ) )
758  {
759  return false;
760  }
761 
762  // Only swap if you're holding a piece and the one you want to pick up isn't
763  // a shared inventory piece
764  if ( isdefined( player.current_craftable_pieces[slot] ) && !‪IS_TRUE( self.piece.is_shared ) )
765  {
766  if ( !level.craftable_piece_swap_allowed )
767  {
768  self.hint_string = &"ZOMBIE_CRAFTABLE_NO_SWITCH";
769  }
770  else
771  {
772  spiece = self.piece;
773  cpiece = player.current_craftable_pieces[slot];
774 
775  if ( spiece.pieceName == cpiece.pieceName && spiece.craftableName == cpiece.craftableName )
776  {
777  self.hint_string = "";
778  return false;
779  }
780 
781  if ( isdefined( self.hint_string_override ) )
782  {
783  self.hint_string = self.hint_string_override;
784  }
785  else
786  {
787  self.hint_string = &"ZOMBIE_BUILD_PIECE_SWITCH";
788  }
789  }
790  }
791  else
792  {
793  if ( isdefined( self.hint_string_override ) )
794  {
795  self.hint_string = self.hint_string_override;
796  }
797  else
798  {
799  self.hint_string = &"ZOMBIE_BUILD_PIECE_GRAB";
800  }
801  }
802 
803  return true;
804 }
805 
807 {
808  self endon( "kill_trigger" );
809 
810  slot = self.stub.piece.inventory_slot;
811 
812  while ( 1 )
813  {
814  self waittill( "trigger", player );
815 
816  // pass the notify along to the stub.
817  self.stub notify( "trigger", player );
818 
819  if ( player != self.parent_player )
820  {
821  continue;
822  }
823 
824  if ( isdefined( player.screecher_weapon ) )
825  {
826  continue;
827  }
828 
829  // Trigger is usable so the message disallowing swapping shows up, but we don't want them to actually
830  // trigger a piece pickup
831  if ( !level.craftable_piece_swap_allowed && isdefined( player.current_craftable_pieces[slot] ) && !‪IS_TRUE( self.stub.piece.is_shared ) )
832  {
833  continue;
834  }
835 
836  if ( !‪zm_utility::is_player_valid( player ) )
837  {
838  player thread ‪zm_utility::ignore_triggers( 0.5 );
839  continue;
840  }
841 
842  status = player ‪player_can_take_piece( self.stub.piece );
843 
844  if ( !status )
845  {
846  self.stub.hint_string = "";
847  self SetHintString( self.stub.hint_string );
848  }
849  else
850  {
851  // The following notify works here, but Evan O. says it's better to put it in player_take_piece(), so we're moving it there. Leaving this here for reference.
852 // player notify( "player_got_craftable_piece_for_" + self.stub.piece.craftableName ); // for VO and other applications.
853  player thread ‪player_take_piece( self.stub.piece );
854  // player_take_piece will end this thread
855  }
856  }
857 }
858 
859 function ‪player_can_take_piece( piece )
860 {
861  if ( !isdefined( piece ) )
862  {
863  return 0;
864  }
865 
866  // okay to take
867  return 1;
868 }
869 
870 function ‪player_throw_piece( piece, origin, dir, return_to_spawn, return_time, endangles )
871 {
872  assert( isdefined( piece ) );
873 
874  if ( isdefined( piece ) )
875  {
876  pass = 0;
877  done = 0;
878  altmodel = undefined;
879 
880  //origin = origin + 0.1 * dir;
881  while ( pass < 2 && !done )
882  {
883  grenade = self MagicGrenadeType( "buildable_piece", origin, dir, 30000 );
884  grenade thread ‪watch_hit_players();
885  grenade Ghost();
886 
887  if ( !isdefined( altmodel ) )
888  {
889  altmodel = ‪spawn( "script_model", grenade.origin );
890  altmodel SetModel( piece.modelName );
891  }
892 
893  altmodel.origin = grenade.angles;
894  altmodel.angles = grenade.angles;
895  altmodel linkTo( grenade, "", ( 0, 0, 0 ), ( 0, 0, 0 ) );
896  grenade.altmodel = altmodel;
897 
898  grenade waittill( "stationary" );
899 
900  grenade_origin = grenade.origin;
901  grenade_angles = grenade.angles;
902 
903  landed_on = grenade GetGroundEnt();
904 
905  grenade delete();
906 
907  if ( isdefined( landed_on ) && landed_on == level )
908  {
909  done = 1;
910  }
911  else
912  {
913  origin = grenade_origin;
914  dir = ( -dir[0] / 10, -dir[1] / 10, -1 );
915  pass++;
916  }
917  }
918 
919  if ( !isdefined( endangles ) )
920  {
921  endangles = grenade_angles;
922  }
923 
924  piece ‪piece_spawn_at( grenade_origin, endangles );
925 
926  if ( isdefined( altmodel ) )
927  {
928  altmodel delete();
929  }
930 
931  if ( isdefined( piece.onDrop ) )
932  {
933  piece [[ piece.onDrop ]]( self );
934  }
935 
936  if ( ‪IS_TRUE( return_to_spawn ) )
937  {
938  piece ‪piece_wait_and_return( return_time );
939  }
940  }
941 }
942 
944 {
945  self endon( "death" );
946  self endon( "stationary" );
947 
948  while ( isdefined( self ) )
949  {
950  self waittill( "grenade_bounce", pos, normal, ent );
951 
952  if ( IsPlayer( ent ) )
953  {
954  ent ‪ExplosionDamage( 25, pos );
955  }
956  }
957 }
958 
959 
960 
961 function ‪piece_wait_and_return( return_time )
962 {
963  self endon( "pickup" );
964  wait 0.15;
965 
966  if ( isdefined( level.exploding_jetgun_fx ) )
967  {
968  PlayFxOnTag( level.exploding_jetgun_fx, self.model, "tag_origin" );
969  }
970  else
971  {
972  PlayFxOnTag( level._effect["powerup_on"], self.model, "tag_origin" );
973  }
974 
975  wait return_time - 6;
976  self ‪piece_hide();
977  wait 1;
978  self ‪piece_show();
979  wait 1;
980  self ‪piece_hide();
981  wait 1;
982  self ‪piece_show();
983  wait 1;
984  self ‪piece_hide();
985  wait 1;
986  self ‪piece_show();
987  wait 1;
988  self notify( "respawn" );
989  self ‪piece_unspawn();
990  self ‪piece_spawn_at();
991 }
992 
994 {
995  self notify( "craftable_piece_released" + slot );
996  piece = self.current_craftable_pieces[slot];
997  self.current_craftable_pieces[slot] = undefined;
998 
999  if ( isdefined( piece ) )
1000  {
1001  piece ‪piece_spawn_at();
1002 
1004  }
1005 }
1006 
1007 
1008 //
1009 // NOTE: "piece_released" was changed to "craftable_piece_released" so it is separate from the buildable
1010 // System. Some buildable stuff still runs automatically, like calling _zm_buildables::OnPlayerLastStand
1011 // from _zm_laststand. That function sends out "piece_released" which would kill functions we're trying
1012 // to run here and process ourselves.
1013 function ‪player_drop_piece_on_death( slot = 0 )
1014 {
1015  self notify( "craftable_piece_released" + slot );
1016  self endon( "craftable_piece_released" + slot );
1017 
1018  // if player dies...
1019  self thread ‪player_drop_piece_on_downed( slot );
1020 
1021  // if player disconnects...
1022  origin = self.origin;
1023  angles = self.angles;
1024  piece = self.current_craftable_pieces[slot];
1025  if ( IsDefined(piece) && isdefined(piece.start_origin) )
1026  {
1027  origin = piece.start_origin;
1028  angles = piece.start_angles;
1029  }
1030  self waittill( "disconnect" );
1031  piece ‪piece_spawn_at( origin, angles );
1032 
1033  if ( isdefined( self ) )
1034  {
1036  }
1037 }
1038 
1039 
1040 function ‪player_drop_piece( piece, slot )
1041 {
1042  if ( !isdefined( piece ) )
1043  {
1044  piece = self.current_craftable_pieces[slot];
1045  }
1046 
1047  if ( isdefined( piece ) )
1048  {
1049  piece.damage = 0;
1050  piece ‪piece_spawn_at( self.origin, self.angles );
1051 
1053 
1054  if ( isdefined( piece.onDrop ) )
1055  {
1056  piece [[ piece.onDrop ]]( self );
1057  }
1058  }
1059 
1060  self.current_craftable_pieces[slot] = undefined;
1061  self notify( "craftable_piece_released" + slot );
1062 }
1063 
1064 
1065 // self == player
1066 function ‪player_take_piece( pieceSpawn )
1067 {
1068  pieceStub = pieceSpawn.pieceStub;
1069  slot = pieceStub.inventory_slot;
1070  ‪damage = pieceSpawn.damage;
1071 
1072  ‪DEFAULT( self.current_craftable_pieces, [] );
1073 
1074  self notify( "player_got_craftable_piece_for_" + pieceSpawn.craftableName ); // for VO and other applications.
1075 
1076  // Don't pick up shared pieces
1077  if ( !‪IS_TRUE( pieceStub.is_shared ) && isdefined( self.current_craftable_pieces[slot] ) )
1078  {
1079  other_piece = self.current_craftable_pieces[slot];
1080  self ‪player_drop_piece( self.current_craftable_piece, slot );
1081  other_piece.damage = ‪damage;
1082  self ‪zm_utility::do_player_general_vox( "general", "craft_swap" );
1083  }
1084 
1085  if ( isdefined( pieceStub.onPickup ) )
1086  {
1087  pieceSpawn [[ pieceStub.onPickup ]]( self );
1088  }
1089 
1090  // Update the UI. The message we send depends on the type of item it is.
1091  // Shared items generate a global update, so they set a world ClientField.
1092  // Unshared items generate a player update so the UI shows what they picked up.
1093  if ( ‪IS_TRUE( pieceStub.is_shared ) )
1094  {
1095  if ( isdefined( pieceStub.client_field_id ) )
1096  {
1097  level ‪clientfield::set( pieceStub.client_field_id, ‪CRAFTABLE_OBTAINED );
1098  }
1099  }
1100  else
1101  {
1102  if ( isdefined( pieceStub.client_field_state ) )
1103  {
1104  self ‪clientfield::set_to_player( ‪CLIENTFIELD_CRAFTABLE, pieceStub.client_field_state );
1105  }
1106  }
1107 
1108  pieceSpawn ‪piece_unspawn();
1109  pieceSpawn notify( "pickup" );
1110 
1111  if ( ‪IS_TRUE( pieceStub.is_shared ) )
1112  {
1113 // pieceStub.in_shared_inventory = true;
1114  pieceSpawn.in_shared_inventory = true; //TODO move this to just pieceStub
1115  }
1116  else
1117  {
1118  slot = pieceSpawn.inventory_slot;
1119  self.current_craftable_pieces[slot] = pieceSpawn;
1120  self thread ‪player_drop_piece_on_death( slot );
1121  }
1122 
1123  // Stat tracking
1124  self ‪track_craftable_piece_pickedup( pieceSpawn );
1125 }
1126 
1127 function ‪player_destroy_piece( piece, slot )
1128 {
1129  if ( !isdefined( piece ) )
1130  {
1131  piece = self.current_craftable_pieces[slot];
1132  }
1133 
1134  if ( isdefined( piece ) )
1135  {
1137  }
1138 
1139  self.current_craftable_pieces[slot] = undefined;
1140  self notify( "craftable_piece_released" + slot );
1141 }
1142 
1143 
1144 function ‪claim_location( location )
1145 {
1146  if ( !isdefined( level.craftable_claimed_locations ) )
1147  {
1148  level.craftable_claimed_locations = [];
1149  }
1150 
1151  if ( !isdefined( level.craftable_claimed_locations[location] ) )
1152  {
1153  level.craftable_claimed_locations[location] = true;
1154  return true;
1155  }
1156 
1157  return false;
1158 }
1159 
1160 // This is VERY expensive - use sparingly
1161 
1163 {
1164  //active_zone_names = level.active_zone_names;//zm_zonemgr::get_active_zone_names();
1165 
1166  candidate_list = [];
1167 
1168  foreach ( zone in level.zones )
1169  {
1170  if ( isdefined( zone.unitrigger_stubs ) )
1171  {
1172  candidate_list = ArrayCombine( candidate_list, zone.unitrigger_stubs, true, false );
1173  }
1174  }
1175 
1176  // we don't care about the dynamic unitriggers
1177  //candidate_list = ArrayCombine(candidate_list, level._unitriggers.dynamic_stubs, true, false);
1178  valid_range = 128;
1179 
1180  closest = ‪zm_unitrigger::get_closest_unitriggers( point, candidate_list, valid_range );
1181 
1182  index = 0;
1183 
1184  while ( index < closest.size )
1185  {
1186  if ( ‪IS_TRUE( closest[index].registered ) && isdefined( closest[index].piece ) )
1187  {
1188  return true;
1189  }
1190 
1191  index++;
1192  }
1193 
1194  return false;
1195 }
1196 
1197 
1198 
1199 //
1200 //
1201 function ‪piece_allocate_spawn( pieceStub )
1202 {
1203  self.current_spawn = 0;
1204  self.managed_spawn = true;
1205  self.pieceStub = pieceStub;
1206 
1207  if ( self.spawns.size >= 1 && self.spawns.size > 1 )
1208  {
1209  any_good = false;
1210  any_okay = false;
1211  totalweight = 0;
1212  spawnweights = [];
1213 
1214  for ( i = 0; i < self.spawns.size; i++ )
1215  {
1216  if ( ‪IS_TRUE( pieceStub.piece_allocated[i] ) )
1217  {
1218  spawnweights[i] = 0;
1219  }
1220  else if ( ‪is_point_in_craft_trigger( self.spawns[i].origin ) )
1221  {
1222  any_okay = true;
1223  spawnweights[i] = 0.01;
1224  }
1225  else
1226  {
1227  any_good = true;
1228  spawnweights[i] = 1.0;
1229  }
1230 
1231  totalweight += spawnweights[i];
1232  }
1233 
1234  if ( any_good )
1235  {
1236  totalweight = float( int( totalweight ) );
1237  }
1238 
1239  r = RandomFloat( totalweight );
1240 
1241  for ( i = 0; i < self.spawns.size; i++ )
1242  {
1243  if ( !any_good || spawnweights[i] >= 1.0 )
1244  {
1245  r -= spawnweights[i];
1246 
1247  if ( r < 0 )
1248  {
1249  self.current_spawn = i;
1250  pieceStub.piece_allocated[self.current_spawn] = true;
1251  return;
1252  }
1253  }
1254  }
1255 
1256  // should never get here
1257  self.current_spawn = RandomInt( self.spawns.size );
1258  pieceStub.piece_allocated[self.current_spawn] = true;
1259  }
1260 }
1261 
1262 
1263 //
1264 // self is a pieceSpawn
1266 {
1267  if ( isdefined( self.current_spawn ) )
1268  {
1269  self.pieceStub.piece_allocated[self.current_spawn] = false;
1270  self.current_spawn = undefined;
1271  }
1272 
1273  self.start_origin = undefined;
1274 }
1275 
1276 
1277 
1279 {
1280  self.current_spawn = 0;
1281 
1282  if ( self.spawns.size >= 1 && self.spawns.size > 1 )
1283  {
1284  self.current_spawn = RandomInt( self.spawns.size );
1285 
1286  while ( isdefined( self.spawns[self.current_spawn].‪claim_location ) &&
1287  !‪claim_location( self.spawns[self.current_spawn].‪claim_location ) )
1288  {
1289  ArrayRemoveIndex( self.spawns, self.current_spawn );
1290 
1291  if ( self.spawns.size < 1 )
1292  {
1293  self.current_spawn = 0;
1294  return;
1295  }
1296 
1297  self.current_spawn = RandomInt( self.spawns.size );
1298  }
1299  }
1300 }
1301 
1302 function ‪piece_set_spawn( num )
1303 {
1304  self.current_spawn = 0;
1305 
1306  if ( self.spawns.size >= 1 && self.spawns.size > 1 )
1307  {
1308  self.current_spawn = int( min( num, self.spawns.size - 1 ) );
1309  }
1310 }
1311 
1312 
1313 //
1314 // self is a pieceSpawn
1315 function ‪piece_spawn_in( pieceStub )
1316 {
1317  if ( self.spawns.size < 1 )
1318  {
1319  return;
1320  }
1321 
1322  if ( ‪IS_TRUE( self.managed_spawn ) )
1323  {
1324  if ( !isdefined( self.current_spawn ) )
1325  {
1326  self ‪piece_allocate_spawn( self.pieceStub );
1327  }
1328  }
1329 
1330  if ( !isdefined( self.current_spawn ) )
1331  {
1332  self.current_spawn = 0;
1333  }
1334 
1335  spawndef = self.spawns[self.current_spawn];
1336 
1337  //TODO: Get a custom KVP to replace script_string
1338  self.unitrigger = ‪generate_piece_unitrigger( "trigger_radius_use", spawndef.origin + ‪PIECE_UNITRIGGER_OFFSET, spawndef.angles, 0, pieceStub.radius, pieceStub.height, pieceStub.hint_string, false, spawndef.script_string );
1339  self.unitrigger.piece = self;
1340 
1341  self.radius = pieceStub.radius;
1342  self.height = pieceStub.height;
1343  self.craftableName = pieceStub.craftableName;
1344  self.pieceName = pieceStub.pieceName;
1345  self.modelName = pieceStub.modelName;
1346  self.hud_icon = pieceStub.hud_icon;
1347  self.tag_name = pieceStub.tag_name;
1348  self.drop_offset = pieceStub.drop_offset;
1349  self.start_origin = spawndef.origin;
1350  self.start_angles = spawndef.angles;
1351  self.client_field_state = pieceStub.client_field_state;
1352  self.is_shared = pieceStub.is_shared;
1353  self.inventory_slot = pieceStub.inventory_slot;
1354 
1355  self.model = ‪Spawn( "script_model", self.start_origin );
1356 
1357  if ( isdefined( self.start_angles ) )
1358  {
1359  self.model.angles = self.start_angles;
1360  }
1361 
1362  self.model SetModel( pieceStub.modelName );
1363 
1364  if ( isdefined( pieceStub.onSpawn ) )
1365  {
1366  self [[ pieceStub.onSpawn ]]();
1367  }
1368 
1369  self.model.hud_icon = pieceStub.hud_icon;
1370  self.pieceStub = pieceStub;
1371 
1372  self.unitrigger.origin_parent = self.model;
1373 
1374  // Cleanup the spawn structs? It would be nice
1375  //self.spawns = undefined;
1376 }
1377 
1378 // self is a piece
1379 function ‪piece_spawn_at( origin, angles, use_random_start )
1380 {
1381  if ( self.spawns.size < 1 )
1382  {
1383  return;
1384  }
1385 
1386  if ( ‪IS_TRUE( self.managed_spawn ) )
1387  {
1388  if ( !isdefined( self.current_spawn ) && !isdefined( origin ) )
1389  {
1390  self ‪piece_allocate_spawn( self.pieceStub );
1391  spawndef = self.spawns[self.current_spawn];
1392  self.start_origin = spawndef.origin;
1393  self.start_angles = spawndef.angles;
1394  }
1395  }
1396  else if ( !isdefined( self.current_spawn ) )
1397  {
1398  self.current_spawn = 0;
1399  }
1400 
1401  unitrigger_offset = ‪PIECE_UNITRIGGER_OFFSET;
1402 
1403  if ( ‪IS_TRUE( use_random_start ) )
1404  {
1406  spawndef = self.spawns[self.current_spawn];
1407  self.start_origin = spawndef.origin;
1408  self.start_angles = spawndef.angles;
1409  origin = spawndef.origin;
1410  angles = spawndef.angles;
1411  }
1412  else
1413  {
1414  if ( !isdefined( origin ) )
1415  {
1416  origin = self.start_origin;
1417  }
1418  else
1419  {
1420  origin = origin + ( 0, 0, self.drop_offset );
1421  unitrigger_offset -= ( 0, 0, self.drop_offset );
1422  }
1423 
1424  if ( !isdefined( angles ) )
1425  {
1426  angles = self.start_angles;
1427  }
1428  }
1429 
1430  self.model = ‪Spawn( "script_model", origin );
1431 
1432  if ( isdefined( angles ) )
1433  {
1434  self.model.angles = angles;
1435  }
1436 
1437  self.model SetModel( self.modelName );
1438 
1439  if ( isdefined( level.equipment_safe_to_drop ) )
1440  {
1441  if ( ! [[level.equipment_safe_to_drop]]( self.model ) )
1442  {
1443  origin = self.start_origin;
1444  angles = self.start_angles;
1445  self.model.origin = origin;
1446  self.model.angles = angles;
1447  }
1448  }
1449 
1450  if ( isdefined( self.‪onSpawn ) )
1451  {
1452  self [[ self.onSpawn ]]();
1453  }
1454 
1455  self.unitrigger = ‪generate_piece_unitrigger( "trigger_radius_use", origin + unitrigger_offset, angles, 0, self.radius, self.height, self.piecestub.hint_string, ‪IS_TRUE( self.model.canMove ) );
1456  self.unitrigger.piece = self;
1457 
1458  self.model.hud_icon = self.hud_icon;
1459 
1460  self.unitrigger.origin_parent = self.model;
1461 }
1462 
1463 
1464 //
1465 // self is a pieceSpawn
1467 {
1468  if ( ‪IS_TRUE( self.managed_spawn ) )
1469  {
1471  }
1472 
1473  if ( isdefined( self.model ) )
1474  {
1475  self.model delete();
1476  }
1477 
1478  self.model = undefined;
1479 
1480  if ( isdefined( self.unitrigger ) )
1481  {
1482  thread ‪zm_unitrigger::unregister_unitrigger( self.unitrigger );
1483  }
1484 
1485  self.unitrigger = undefined;
1486 }
1487 
1488 function ‪piece_hide()
1489 {
1490  if ( isdefined( self.model ) )
1491  {
1492  self.model Ghost();
1493  }
1494 }
1495 
1496 function ‪piece_show()
1497 {
1498  if ( isdefined( self.model ) )
1499  {
1500  self.model Show();
1501  }
1502 }
1503 
1504 
1505 //
1506 // returns a pieceSpawn
1507 function ‪generate_piece( pieceStub )
1508 {
1509  pieceSpawn = spawnstruct();
1510  pieceSpawn.spawns = pieceStub.spawns;
1511 
1512  if ( ‪IS_TRUE( pieceStub.managing_pieces ) )
1513  {
1514  pieceSpawn ‪piece_allocate_spawn( pieceStub );
1515  }
1516  else if ( isdefined( pieceStub.use_spawn_num ) )
1517  {
1518  pieceSpawn ‪piece_set_spawn( pieceStub.use_spawn_num );
1519  }
1520  else
1521  {
1522  pieceSpawn ‪piece_pick_random_spawn();
1523  }
1524 
1525  // special spawn to allow for fx and trigger changes to pickup parts.
1526  if( isdefined( pieceStub.special_spawn_func ) )
1527  {
1528  pieceSpawn [[pieceStub.special_spawn_func]]( pieceStub ) ;
1529  }
1530  else
1531  {
1532  pieceSpawn ‪piece_spawn_in( pieceStub );
1533  }
1534 
1535  if ( pieceSpawn.spawns.size >= 1 )
1536  {
1537  pieceSpawn.hud_icon = pieceStub.hud_icon;
1538  }
1539 
1540  if ( isdefined( pieceStub.onPickup ) )
1541  {
1542  pieceSpawn.onPickup = pieceStub.onPickup;
1543  }
1544  else
1545  {
1546  pieceSpawn.onPickup = &‪onPickupUTS;
1547  }
1548 
1549  if ( isdefined( pieceStub.onDrop ) )
1550  {
1551  pieceSpawn.onDrop = pieceStub.onDrop;
1552  }
1553  else
1554  {
1555  pieceSpawn.onDrop = &‪onDropUTS;
1556  }
1557 
1558  if ( isdefined( pieceStub.onCrafted ) )
1559  {
1560  pieceSpawn.onCrafted = pieceStub.onCrafted;
1561  }
1562 
1563  return pieceSpawn;
1564 }
1565 
1566 
1567 //
1568 // self is a unitrigger stub
1569 function ‪craftable_piece_unitriggers( craftable_name, origin )
1570 {
1571 
1572  Assert( isdefined( craftable_name ) );
1573  Assert( isdefined( level.zombie_craftableStubs[ craftable_name ] ), "Called craftable_think() without including the craftable - " + craftable_name );
1574 
1575  // Grab Craftable
1576  //---------------
1577  craftable = level.zombie_craftableStubs[ craftable_name ];
1578 
1579  if ( !isdefined( craftable.a_pieceStubs ) )
1580  {
1581  craftable.a_pieceStubs = [];
1582  }
1583 
1584  // Need To Wait For Some Scripts To Be Set Up Before Proceeding, Best Case Notify
1585  //-------------------------------------------------------------------------------
1586  level ‪flag::wait_till( "start_zombie_round_logic" );
1587 
1588  craftableSpawn = spawnstruct();
1589 
1590  craftableSpawn.craftable_name = craftable_name;
1591 
1592  if ( !isdefined( craftableSpawn.a_pieceSpawns ) )
1593  {
1594  craftableSpawn.a_pieceSpawns = [];
1595  }
1596 
1597  // Create A Carry Objects (One Per Each Type Currently)
1598  //----------------------------------------------------
1599  craftablePickUps = [];
1600 
1601  foreach ( pieceStub in craftable.a_pieceStubs )
1602  {
1603  ‪DEFAULT( craftableSpawn.inventory_slot, pieceStub.inventory_slot );
1604  if ( !isdefined( pieceStub.generated_instances ) )
1605  {
1606  pieceStub.generated_instances = 0;
1607  }
1608 
1609  // If this craftable is re-using parts from another craftable, use the previously generated piece
1610  if ( isdefined( pieceStub.pieceSpawn ) && ‪IS_TRUE( pieceStub.can_reuse ) )
1611  {
1612  piece = pieceStub.pieceSpawn;
1613  }
1614  else if ( pieceStub.generated_instances >= pieceStub.max_instances )
1615  {
1616  piece = pieceStub.pieceSpawn;
1617  }
1618  else
1619  {
1620  piece = ‪generate_piece( pieceStub );
1621  pieceStub.pieceSpawn = piece;
1622  pieceStub.generated_instances++;
1623  }
1624 
1625  craftableSpawn.a_pieceSpawns[craftableSpawn.a_pieceSpawns.size] = piece;
1626  }
1627 
1628  craftableSpawn.stub = self;
1629 
1630  return craftableSpawn;
1631 }
1632 
1633 function ‪hide_craftable_table_model( trigger_targetname )
1634 {
1635  trig = GetEnt( trigger_targetname, "targetname" );
1636 
1637  if ( !isdefined( trig ) )
1638  {
1639  return;
1640  }
1641 
1642  if ( isdefined( trig.target ) )
1643  {
1644  model = getent( trig.target, "targetname" );
1645 
1646  if ( isdefined( model ) )
1647  {
1648  model Ghost();
1649  model NotSolid();
1650  }
1651  }
1652 }
1653 
1654 // self is a craftable stub (CSt)
1655 function ‪setup_unitrigger_craftable( trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
1656 {
1657  trig = GetEnt( trigger_targetname, "targetname" );
1658 
1659  if ( !isdefined( trig ) )
1660  {
1661  return;
1662  }
1663 
1664  return ‪setup_unitrigger_craftable_internal( trig, equipname, weaponname, trigger_hintstring, delete_trigger, persistent );
1665 }
1666 
1667 // self is a craftable stub (CSt)
1668 function ‪setup_unitrigger_craftable_array( trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
1669 {
1670  triggers = GetEntArray( trigger_targetname, "targetname" );
1671  stubs = [];
1672 
1673  foreach ( trig in triggers )
1674  {
1675  stubs[stubs.size] = ‪setup_unitrigger_craftable_internal( trig, equipname, weaponname, trigger_hintstring, delete_trigger, persistent );
1676  }
1677 
1678  return stubs;
1679 }
1680 
1681 
1682 // self is a craftable stub (CSt)
1683 function ‪setup_unitrigger_craftable_internal( trig, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
1684 {
1685  if ( !isdefined( trig ) )
1686  {
1687  return;
1688  }
1689 
1690  unitrigger_stub = spawnstruct();
1691 
1692  unitrigger_stub.craftableStub = level.zombie_include_craftables[ equipname ];
1693 
1694  angles = trig.script_angles;
1695 
1696  if ( !isdefined( angles ) )
1697  {
1698  angles = ( 0, 0, 0 );
1699  }
1700 
1701  unitrigger_stub.origin = trig.origin + ( anglestoright( angles ) * -6 );
1702 
1703  unitrigger_stub.angles = trig.angles;
1704 
1705  if ( isdefined( trig.script_angles ) )
1706  {
1707  unitrigger_stub.angles = trig.script_angles;
1708  }
1709 
1710  unitrigger_stub.equipname = equipname;
1711  unitrigger_stub.weaponname = GetWeapon( weaponname );
1712  unitrigger_stub.trigger_hintstring = trigger_hintstring;
1713  unitrigger_stub.delete_trigger = delete_trigger;
1714  unitrigger_stub.crafted = false;
1715  unitrigger_stub.persistent = persistent;
1716  unitrigger_stub.useTime = int( 3 * 1000 );
1717 
1718  if ( isdefined( self.useTime ) )
1719  {
1720  unitrigger_stub.useTime = self.useTime;
1721  }
1722  else if ( isdefined( trig.useTime ) )
1723  {
1724  unitrigger_stub.useTime = trig.useTime;
1725  }
1726 
1727  unitrigger_stub.onBeginUse = &‪onBeginUseUTS;
1728  unitrigger_stub.onEndUse = &‪onEndUseUTS;
1729  unitrigger_stub.onUse = &‪onUsePlantObjectUTS;
1730  unitrigger_stub.onCantUse = &‪onCantUseUTS;
1731 
1732  tmins = trig GetMins();
1733  tmaxs = trig GetMaxs();
1734  tsize = tmaxs-tmins;
1735 
1736  if ( isdefined( trig.script_depth ) )
1737  {
1738  unitrigger_stub.script_length = trig.script_depth;
1739  }
1740  else
1741  {
1742  unitrigger_stub.script_length = tsize[1];
1743  }
1744 
1745  if ( isdefined( trig.script_width ) )
1746  {
1747  unitrigger_stub.script_width = trig.script_width;
1748  }
1749  else
1750  {
1751  unitrigger_stub.script_width = tsize[0];
1752  }
1753 
1754  if ( isdefined( trig.script_height ) )
1755  {
1756  unitrigger_stub.script_height = trig.script_height;
1757  }
1758  else
1759  {
1760  unitrigger_stub.script_height = tsize[2];
1761  }
1762 
1763  unitrigger_stub.target = trig.target;
1764  unitrigger_stub.targetname = trig.targetname;
1765 
1766  unitrigger_stub.script_noteworthy = trig.script_noteworthy;
1767  unitrigger_stub.script_parameters = trig.script_parameters;
1768 
1769  unitrigger_stub.cursor_hint = "HINT_NOICON";
1770 
1771  if ( isdefined( level.zombie_craftableStubs[ equipname ].str_to_craft ) )
1772  {
1773  unitrigger_stub.hint_string = level.zombie_craftableStubs[ equipname ].str_to_craft;
1774  }
1775 
1776  unitrigger_stub.script_unitrigger_type = "unitrigger_box_use";
1777  unitrigger_stub.require_look_at = true;
1778  unitrigger_stub.require_look_toward = false;
1779 
1781 
1782  if ( isdefined( unitrigger_stub.craftableStub.custom_craftablestub_update_prompt ) )
1783  {
1784  unitrigger_stub.custom_craftablestub_update_prompt = unitrigger_stub.craftableStub.custom_craftablestub_update_prompt;
1785  }
1786 
1787  unitrigger_stub.prompt_and_visibility_func = &‪craftabletrigger_update_prompt;
1788 
1790 
1791  unitrigger_stub.piece_trigger = trig;
1792  trig.trigger_stub = unitrigger_stub;
1793 
1794  if ( isdefined( trig.zombie_weapon_upgrade ) )
1795  {
1796  unitrigger_stub.zombie_weapon_upgrade = GetWeapon( trig.zombie_weapon_upgrade );
1797  }
1798 
1799  if ( isdefined( unitrigger_stub.target ) )
1800  {
1801  unitrigger_stub.model = getent( unitrigger_stub.target, "targetname" );
1802 
1803  if ( isdefined( unitrigger_stub.model ) )
1804  {
1805  if ( isdefined( unitrigger_stub.zombie_weapon_upgrade ) )
1806  {
1807  unitrigger_stub.model useweaponhidetags( unitrigger_stub.zombie_weapon_upgrade );
1808  }
1809 
1810  // Checks for starting parameters on craftable model
1811  // Parameters:
1812  // starts_visible - craftable model starts visible
1813  // starts_empty - hides all or some craft pieces on craftable model
1814  if ( isdefined( unitrigger_stub.model.script_parameters ) )
1815  {
1816  a_utm_params = strtok( unitrigger_stub.model.script_parameters, " ");
1817  foreach ( param in a_utm_params )
1818  {
1819  if ( param == "starts_visible" )
1820  {
1821  b_start_visible = true;
1822  continue;
1823  }
1824  else if ( param == "starts_empty" )
1825  {
1826  b_start_empty = true;
1827  }
1828  }
1829  }
1830 
1831  // If craftable target has parameter "starts_visible", then do not hide craftable target
1832  if ( b_start_visible !== true )
1833  {
1834  unitrigger_stub.model Ghost();
1835  unitrigger_stub.model NotSolid();
1836  }
1837  }
1838  }
1839 
1840  // working vars for open craftable tables
1841  if ( unitrigger_stub.equipname == "open_table" )
1842  {
1843  unitrigger_stub.a_uts_open_craftables_available = [];
1844  unitrigger_stub.n_open_craftable_choice = ‪CRAFTABLE_INVALID_CHOICE;
1845  unitrigger_stub.b_open_craftable_checking_input = false;
1846  }
1847 
1848  // create craftable pieces
1849  unitrigger_stub.craftableSpawn = unitrigger_stub ‪craftable_piece_unitriggers( equipname, unitrigger_stub.origin );
1850 
1851  // If craftable target has parameter "starts_empty", then hide all uncrafted joint parts
1852  if ( isdefined( unitrigger_stub.model ) && b_start_empty === true )
1853  {
1854  for ( i = 0; i < unitrigger_stub.craftableSpawn.a_pieceSpawns.size; i++ )
1855  {
1856  if ( isdefined( unitrigger_stub.craftableSpawn.a_pieceSpawns[i].tag_name ) )
1857  {
1858  if ( unitrigger_stub.craftableSpawn.a_pieceSpawns[i].crafted !== true )
1859  {
1860  unitrigger_stub.model HidePart( unitrigger_stub.craftableSpawn.a_pieceSpawns[i].tag_name );
1861  }
1862  else
1863  {
1864  // For objects starting partially crafted
1865  unitrigger_stub.model ShowPart( unitrigger_stub.craftableSpawn.a_pieceSpawns[i].tag_name );
1866  }
1867  }
1868  }
1869  }
1870 
1871  if ( delete_trigger )
1872  {
1873  trig delete();
1874  }
1875 
1876  level.a_uts_craftables[level.a_uts_craftables.size] = unitrigger_stub;
1877  return unitrigger_stub;
1878 }
1879 
1880 
1881 //
1882 // Setup the pieces only. No craftable trigger involved. ie, if this is just shared stuff you pick up
1883 // self is craftable data (CD)
1885 {
1886  unitrigger_stub = spawnstruct();
1887 
1888  unitrigger_stub.craftableStub = level.zombie_include_craftables[ self.name ];
1889 
1890  unitrigger_stub.equipname = self.name;
1891 // unitrigger_stub.weaponname = self.name;
1892 
1893  // create craftable pieces
1894  unitrigger_stub.craftableSpawn = unitrigger_stub ‪craftable_piece_unitriggers( self.‪name, unitrigger_stub.origin );
1895 
1896  level.a_uts_craftables[level.a_uts_craftables.size] = unitrigger_stub;
1897  return unitrigger_stub;
1898 }
1899 
1900 
1901 //
1902 // self is a craftableSpawn
1903 function ‪craftable_has_piece( piece )
1904 {
1905  for ( i = 0; i < self.a_pieceSpawns.size; i++ )
1906  if ( self.a_pieceSpawns[i].pieceName == piece.pieceName && self.a_pieceSpawns[i].craftablename == piece.craftablename )
1907  {
1908  return true;
1909  }
1910 
1911  return false;
1912 }
1913 
1914 
1915 // If we're crafting at an open table, we'll need to set the values for the
1916 // actual craftable we're trying to craft, which would not be the "open_table".
1917 // The open_craftableSpawn will be set via the trigger update
1918 // returns a uts_craftable
1919 // self is a uts_craftableSpawn
1921 {
1922  if ( self.craftable_name == "open_table" &&
1923  self.n_open_craftable_choice != ‪CRAFTABLE_INVALID_CHOICE )
1924  {
1925  return self.stub.a_uts_open_craftables_available[ self.n_open_craftable_choice ];
1926  }
1927  else
1928  {
1929  // Normal craftable, proceed as normal
1930  return self.stub;
1931  }
1932 }
1933 
1934 
1935 // If we're crafting at an open table, we'll need to set the values for the
1936 // actual craftable we're trying to craft, which would not be the "open_table".
1937 // The open_craftableSpawn will be set via the trigger update
1938 // returns a uts_craftableSpawn
1939 // self is a uts_craftableSpawn
1941 {
1942  if ( self.craftable_name == "open_table" &&
1943  self.stub.n_open_craftable_choice != ‪CRAFTABLE_INVALID_CHOICE &&
1944  isdefined( self.stub.a_uts_open_craftables_available[ self.stub.n_open_craftable_choice ].craftableSpawn ) )
1945  {
1946  return self.stub.a_uts_open_craftables_available[ self.stub.n_open_craftable_choice ].craftableSpawn;
1947  }
1948  else
1949  {
1950  // Normal craftable, proceed as normal
1951  return self;
1952  }
1953 }
1954 
1955 
1956 //
1957 // Returns true if a shared item has been obtained but not used.
1958 // self is a craftableSpawn
1960 {
1961  // We've already determined that we can use something at this open table
1962  uts_craftable = self.stub;
1963 
1964  if ( isdefined( uts_craftable.n_open_craftable_choice ) &&
1965  uts_craftable.n_open_craftable_choice != ‪CRAFTABLE_INVALID_CHOICE &&
1966  isdefined( uts_craftable.a_uts_open_craftables_available[ uts_craftable.n_open_craftable_choice ] ) )
1967  {
1968  return true;
1969  }
1970 
1971  // Check if we need all pieces (assuming all are shared)
1972  if ( ‪IS_TRUE( uts_craftable.craftableStub.need_all_pieces ) )
1973  {
1974  // If all shared pieces are in the inventory, then we can build it.
1975  foreach ( piece in self.a_pieceSpawns )
1976  {
1977  if ( !‪IS_TRUE( piece.in_shared_inventory ) )
1978  {
1979  return false;
1980  }
1981  }
1982 
1983  return true;
1984  }
1985  // Otherwise one shared piece is enough
1986  else
1987  {
1988  foreach ( piece in self.a_pieceSpawns )
1989  {
1990  if ( !‪IS_TRUE( piece.crafted ) && ‪IS_TRUE( piece.in_shared_inventory ) )
1991  {
1992  return true;
1993  }
1994  }
1995  }
1996 
1997  return false;
1998 }
1999 
2000 
2001 // Mark pieces as having been applied to the table, ie "crafted"
2002 // self is a craftableSpawn
2003 function ‪craftable_set_piece_crafted( pieceSpawn_check, player )
2004 {
2005  craftableSpawn_check = ‪get_actual_craftableSpawn();
2006 
2007  foreach ( pieceSpawn in craftableSpawn_check.a_pieceSpawns )
2008  {
2009  if ( isdefined( pieceSpawn_check ) )
2010  {
2011  if ( pieceSpawn.pieceName == pieceSpawn_check.pieceName &&
2012  pieceSpawn.craftablename == pieceSpawn_check.craftablename )
2013  {
2014  pieceSpawn.crafted = true;
2015 
2016  // Run onCrafted func
2017  if ( isdefined( pieceSpawn.onCrafted ) )
2018  {
2019  pieceSpawn thread [[pieceSpawn.onCrafted]]( player );
2020  }
2021 
2022  continue;
2023  }
2024  }
2025 
2026  // Check to see if it's a shared piece, and if so, mark it as crafted
2027  if ( ‪IS_TRUE( pieceSpawn.is_shared ) && ‪IS_TRUE( pieceSpawn.in_shared_inventory ) )
2028  {
2029  pieceSpawn.crafted = true;
2030 
2031  // Run onCrafted func
2032  if ( isdefined( pieceSpawn.onCrafted ) )
2033  {
2034  pieceSpawn thread [[pieceSpawn.onCrafted]]( player );
2035  }
2036 
2037  // Remove from inventory so we don't run onCrafted again.
2038  pieceSpawn.in_shared_inventory = false;
2039  }
2040  }
2041 }
2042 
2043 // Mark pieces as currently being applied to the table, ie "crafting"
2044 // self is a craftableSpawn
2045 function ‪craftable_set_piece_crafting( pieceSpawn_check )
2046 {
2047  craftableSpawn_check = ‪get_actual_craftableSpawn();
2048 
2049  foreach ( pieceSpawn in craftableSpawn_check.a_pieceSpawns )
2050  {
2051  if ( isdefined( pieceSpawn_check ) )
2052  {
2053  if ( pieceSpawn.pieceName == pieceSpawn_check.pieceName &&
2054  pieceSpawn.craftablename == pieceSpawn_check.craftablename )
2055  {
2056  pieceSpawn.crafting = true;
2057  }
2058  }
2059 
2060  // Check to see if it's a shared piece, and if so, mark it as crafted
2061  if ( ‪IS_TRUE( pieceSpawn.is_shared ) && ‪IS_TRUE( pieceSpawn.in_shared_inventory ) )
2062  {
2063  pieceSpawn.crafting = true;
2064  }
2065  }
2066 }
2067 
2068 
2069 // Mark pieces as not in process of "crafting"
2070 // self is a craftableSpawn
2071 function ‪craftable_clear_piece_crafting( pieceSpawn_check )
2072 {
2073  if ( isdefined( pieceSpawn_check ) )
2074  {
2075  pieceSpawn_check.crafting = false;
2076  }
2077 
2078  craftableSpawn_check = ‪get_actual_craftableSpawn();
2079 
2080  foreach ( pieceSpawn in craftableSpawn_check.a_pieceSpawns )
2081  {
2082  // Check for shared pieces
2083  if ( ‪IS_TRUE( pieceSpawn.is_shared ) && ‪IS_TRUE( pieceSpawn.in_shared_inventory ) )
2084  {
2085  pieceSpawn.crafting = false;
2086  }
2087  }
2088 }
2089 
2091 {
2092  for ( i = 0; i < self.a_pieceSpawns.size; i++ )
2093  {
2094  if ( self.a_pieceSpawns[i].pieceName == piece.pieceName && self.a_pieceSpawns[i].craftablename == piece.craftablename )
2095  {
2096  return ‪IS_TRUE( self.a_pieceSpawns[i].crafted );
2097  }
2098  }
2099 
2100  return false;
2101 }
2102 
2103 
2105 {
2106  if( !IsDefined(level.shared_crafting_in_progress) )
2107  level.shared_crafting_in_progress = self;
2108 }
2109 
2111 {
2112  if ( ‪IS_EQUAL(self,level.shared_crafting_in_progress) )
2113  level.shared_crafting_in_progress = undefined;
2114 }
2115 
2116 function ‪can_craft_shared_piece( continuing )
2117 {
2118  if ( continuing )
2119  return ( ‪IS_EQUAL( self, level.shared_crafting_in_progress) );
2120  return !IsDefined( level.shared_crafting_in_progress);
2121 }
2122 
2123 // Is a piece in the process of "crafting"?
2124 // self is a craftableSpawn
2125 function ‪craftable_is_piece_crafting( pieceSpawn_check )
2126 {
2127  craftableSpawn_check = ‪get_actual_craftableSpawn();
2128 
2129  foreach ( pieceSpawn in craftableSpawn_check.a_pieceSpawns )
2130  {
2131  if ( isdefined( pieceSpawn_check ) )
2132  {
2133  if ( pieceSpawn.pieceName == pieceSpawn_check.pieceName &&
2134  pieceSpawn.craftablename == pieceSpawn_check.craftablename )
2135  {
2136  return pieceSpawn.crafting;
2137  }
2138  }
2139 
2140  // Check for shared pieces
2141  if ( ‪IS_TRUE( pieceSpawn.is_shared ) &&
2142  ‪IS_TRUE( pieceSpawn.in_shared_inventory ) &&
2143  ‪IS_TRUE( pieceSpawn.crafting ) )
2144  {
2145  return true;
2146  }
2147  }
2148 
2149  return false;
2150 }
2151 
2153 {
2154  for ( i = 0; i < self.a_pieceSpawns.size; i++ )
2155  if ( self.a_pieceSpawns[i].pieceName == piece.pieceName && self.a_pieceSpawns[i].craftablename == piece.craftablename )
2156  {
2157  return ‪IS_TRUE( self.a_pieceSpawns[i].crafted ) || ‪IS_TRUE( self.a_pieceSpawns[i].crafting );
2158  }
2159 
2160  return false;
2161 }
2162 
2164 {
2165  if ( ‪IS_TRUE( self.stub.craftableStub.need_all_pieces ) )
2166  {
2167  // If all shared pieces are in the inventory, then we can build it.
2168  foreach ( piece in self.a_pieceSpawns )
2169  {
2170  if ( !‪IS_TRUE( piece.in_shared_inventory ) && !piece.crafted )//Make sure we also check to see if it's already been crafted and cleaned up
2171  {
2172  return false;
2173  }
2174  }
2175 
2176  return true;
2177  }
2178 
2179  for ( i = 0; i < self.a_pieceSpawns.size; i++ )
2180  if ( !‪IS_TRUE( self.a_pieceSpawns[i].crafted ) )
2181  {
2182  return false;
2183  }
2184 
2185  return true;
2186 }
2187 
2188 function ‪waittill_crafted( craftable_name )
2189 {
2190  level waittill( craftable_name + "_crafted", player );
2191  return player;
2192 }
2193 
2194 
2195 function ‪player_can_craft( craftableSpawn, continuing, slot )
2196 {
2197  if ( !isdefined( craftableSpawn ) )
2198  {
2199  return 0;
2200  }
2201 
2202  if ( !isdefined( slot ) )
2203  {
2204  slot = craftableSpawn.inventory_slot;
2205  }
2206 
2207  if ( !craftableSpawn ‪craftable_can_use_shared_piece() )
2208  {
2209  if ( !isdefined( slot ) )
2210  {
2211  return 0;
2212  }
2213 
2214  if ( !isdefined( self.current_craftable_pieces[slot] ) )
2215  {
2216  return 0;
2217  }
2218 
2219  if ( !craftableSpawn ‪craftable_has_piece( self.current_craftable_pieces[slot] ) )
2220  {
2221  return 0;
2222  }
2223 
2224  if ( ‪IS_TRUE( continuing ) )
2225  {
2226  if ( craftableSpawn ‪craftable_is_piece_crafted( self.current_craftable_pieces[slot] ) )
2227  {
2228  return 0;
2229  }
2230  }
2231  else
2232  {
2233  if ( craftableSpawn ‪craftable_is_piece_crafted_or_crafting( self.current_craftable_pieces[slot] ) )
2234  {
2235  return 0;
2236  }
2237  }
2238  }
2239  else
2240  {
2241  if ( ‪IS_TRUE(craftableSpawn.stub.crafted ) && !continuing )
2242  {
2243  return 0;
2244  }
2245 
2246  if ( craftableSpawn.stub.useTime > 0 && !self ‪can_craft_shared_piece(continuing) )
2247  {
2248  return 0;
2249  }
2250  }
2251 
2252 
2253  // NOTE: custom_craftablestub_update_prompt is called with the following parameters:
2254  // ( player, b_set_hint_string_now, trigger )
2255  if ( isdefined( craftableSpawn.stub ) &&
2256  isdefined( craftableSpawn.stub.custom_craftablestub_update_prompt ) &&
2257  isdefined( craftableSpawn.stub.playertrigger[0] ) &&
2258  isdefined( craftableSpawn.stub.playertrigger[0].stub ) &&
2259  !craftableSpawn.stub.playertrigger[ 0 ].stub [[ craftableSpawn.stub.custom_craftablestub_update_prompt ]]( self, true, craftableSpawn.stub.playertrigger[ self GetEntityNumber() ] ) )
2260  {
2261  return 0;
2262  }
2263 
2264  // okay to craft
2265  return 1;
2266 }
2267 
2268 
2269 //
2270 // This transfers the current table's trigger data to the craftable
2271 // we're crafting open craftable.
2272 // self is a craftableSpawn
2274 {
2275  uts_craftable = self.stub;
2276 
2277  if ( uts_craftable.n_open_craftable_choice == ‪CRAFTABLE_INVALID_CHOICE ||
2278  !isdefined( uts_craftable.a_uts_open_craftables_available[ uts_craftable.n_open_craftable_choice ] ) )
2279  {
2280  return;
2281  }
2282 
2283  // source craftable (the thing we're crafting)
2284  uts_source = uts_craftable.a_uts_open_craftables_available[ uts_craftable.n_open_craftable_choice ];
2285 
2286  // Check to see if the item crafted was at an open craft table
2287  uts_target = uts_craftable;
2288 
2289  uts_target.craftableStub = uts_source.craftableStub;
2290  uts_target.craftableSpawn = uts_source.craftableSpawn;
2291  uts_target.crafted = uts_source.crafted;
2292  uts_target.cursor_hint = uts_source.cursor_hint;
2293  uts_target.custom_craftable_update_prompt = uts_source.custom_craftable_update_prompt;
2294  uts_target.equipname = uts_source.equipname;
2295  uts_target.hint_string = uts_source.hint_string;
2296  uts_target.persistent = uts_source.persistent;
2297  uts_target.prompt_and_visibility_func = uts_source.prompt_and_visibility_func;
2298  uts_target.trigger_func = uts_source.trigger_func;
2299  uts_target.trigger_hintstring = uts_source.trigger_hintstring;
2300  uts_target.weaponname = uts_source.weaponname;
2301 
2302  // Make sure to point the stub back to the correct uts_craftable
2303  uts_target.craftableSpawn.stub = uts_target;
2304 
2305  // unregister the old trigger loc
2306  thread ‪zm_unitrigger::unregister_unitrigger( uts_source );
2307  uts_source ‪craftablestub_remove();
2308 
2309  return uts_target;
2310 }
2311 
2312 
2313 // A player has just crafted a piece.
2314 // self is a player
2315 function ‪player_craft( craftableSpawn, slot = craftableSpawn.inventory_slot )
2316 {
2317  ‪DEFAULT( self.current_craftable_pieces, [] );
2318 
2319  if ( isdefined( slot ) )
2320  {
2321  craftableSpawn ‪craftable_set_piece_crafted( self.current_craftable_pieces[slot], self );
2322  }
2323 
2324  // If the current piece was used in crafting, then kill it.
2325  // Note, if we were crafting shared items, the current piece
2326  // could be unrelated, so we would want to keep it.
2327  if ( isdefined( slot ) &&
2328  isdefined( self.current_craftable_pieces[slot] ) &&
2329  ‪IS_TRUE( self.current_craftable_pieces[slot].crafted ) )
2330  {
2331  ‪player_destroy_piece( self.current_craftable_pieces[slot], slot );
2332  }
2333 
2334  // Did we just craft at an open table?
2335  if ( isdefined( craftableSpawn.stub.n_open_craftable_choice ) )
2336  {
2337  uts_craftable = craftableSpawn ‪craftable_transfer_data();
2338  craftableSpawn = uts_craftable.craftableSpawn;
2340  }
2341  else
2342  {
2343  uts_craftable = craftableSpawn.stub;
2344  }
2345 
2346  // See if we need to spawn in a model
2347  if ( !isdefined( uts_craftable.model ) && isdefined( uts_craftable.craftableStub.str_model ) )
2348  {
2349  craftableStub = uts_craftable.craftableStub;
2350  s_model = ‪struct::get( uts_craftable.target, "targetname" );
2351 
2352  if ( isdefined( s_model ) )
2353  {
2354  m_spawn = ‪Spawn( "script_model", s_model.origin );
2355 
2356  if ( isdefined( craftableStub.v_origin_offset ) )
2357  {
2358  m_spawn.origin += craftableStub.v_origin_offset;
2359  }
2360 
2361  m_spawn.angles = s_model.angles;
2362 
2363  if ( isdefined( craftableStub.v_angle_offset ) )
2364  {
2365  m_spawn.angles += craftableStub.v_angle_offset;
2366  }
2367 
2368  m_spawn SetModel( craftableStub.str_model );
2369  uts_craftable.model = m_spawn;
2370  }
2371  }
2372 
2373  if ( isdefined( uts_craftable.model ) )
2374  {
2375  for ( i = 0; i < craftableSpawn.a_pieceSpawns.size; i++ )
2376  {
2377  if ( isdefined( craftableSpawn.a_pieceSpawns[i].tag_name ) )
2378  {
2379  uts_craftable.model NotSolid();
2380 
2381  if ( !‪IS_TRUE( craftableSpawn.a_pieceSpawns[i].crafted ) )
2382  {
2383  uts_craftable.model HidePart( craftableSpawn.a_pieceSpawns[i].tag_name );
2384  }
2385  else
2386  {
2387  uts_craftable.model show();
2388  uts_craftable.model ShowPart( craftableSpawn.a_pieceSpawns[i].tag_name );
2389  }
2390  }
2391  }
2392  }
2393 
2394  //stat tracking
2395  self ‪track_craftable_pieces_crafted( craftableSpawn );
2396 
2397  if ( craftableSpawn ‪craftable_all_crafted() )
2398  {
2399  self ‪player_finish_craftable( craftableSpawn );
2400  //craftableSpawn.stub craftablestub_finish_craft(self);
2401 
2402  //stat tracking
2403  self ‪track_craftables_crafted( craftableSpawn );
2404 
2405  //SQ
2406  if ( isdefined( level.craftable_crafted_custom_func ) )
2407  {
2408  self thread [[level.craftable_crafted_custom_func]]( craftableSpawn );
2409  }
2410 
2411  //self playsound( "zmb_buildable_complete" );
2412 
2413  // at this point either the trigger will be deleted or the prompt will be changed to the buy prompt so the crafted prompt will never appear in practice
2414  //assert (isdefined(level.zombie_craftableStubs[ craftableSpawn.craftable_name ].crafted), "Missing crafted hint" );
2415  //if (isdefined(level.zombie_craftableStubs[ craftableSpawn.craftable_name ].crafted))
2416  // return level.zombie_craftableStubs[ craftableSpawn.craftable_name ].crafted;
2417  }
2418  else
2419  {
2420  self playsound( "zmb_buildable_piece_add" );
2421 
2422  assert ( isdefined( level.zombie_craftableStubs[ craftableSpawn.craftable_name ].str_crafting ), "Missing builing hint" );
2423 
2424  if ( isdefined( level.zombie_craftableStubs[ craftableSpawn.craftable_name ].str_crafting ) )
2425  {
2426  return level.zombie_craftableStubs[ craftableSpawn.craftable_name ].str_crafting;
2427  }
2428  }
2429 
2430  return "";
2431 }
2432 
2433 
2434 //
2435 // Check to see if there's any open craftables remaining.
2436 // If there isn't, shut down any remaining open craftable tables
2438 {
2439  b_open_craftables_remaining = false;
2440 
2441  foreach ( uts_craftable in level.a_uts_craftables )
2442  {
2443  if ( isdefined( level.zombie_include_craftables[ uts_craftable.equipname ] ) &&
2444  ‪IS_TRUE( level.zombie_include_craftables[ uts_craftable.equipname ].is_open_table ) )
2445  {
2446  // Check to see if any pieces have been crafted
2447  b_piece_crafted = false;
2448 
2449  foreach ( pieceSpawn in uts_craftable.craftableSpawn.a_pieceSpawns )
2450  {
2451  if ( ‪IS_TRUE( pieceSpawn.crafted ) )
2452  {
2453  b_piece_crafted = true;
2454  break;
2455  }
2456  }
2457 
2458  if ( !b_piece_crafted )
2459  {
2460  b_open_craftables_remaining = true;
2461  }
2462  }
2463  }
2464 
2465  // If there's no more open craftables to craft, shut down the open tables
2466  if ( !b_open_craftables_remaining )
2467  {
2468  foreach ( uts_craftable in level.a_uts_craftables )
2469  {
2470  if ( uts_craftable.equipname == "open_table" )
2471  {
2472  thread ‪zm_unitrigger::unregister_unitrigger( uts_craftable );
2473  }
2474  }
2475  }
2476 }
2477 
2478 
2479 // Mark craftable as having been completed
2480 function ‪player_finish_craftable( craftableSpawn )
2481 {
2482  craftableSpawn.crafted = true;
2483  craftableSpawn.stub.crafted = true;
2484  craftableSpawn notify( "crafted", self );
2485  level.craftables_crafted[ craftableSpawn.craftable_name ] = true;
2486  level notify( craftableSpawn.craftable_name + "_crafted", self );
2487 }
2488 
2489 //
2490 // magically finish a craftable. For debug purposes only. Some side effects may occur
2491 function ‪complete_craftable( str_craftable_name )
2492 {
2493  foreach ( uts_craftable in level.a_uts_craftables )
2494  {
2495  if ( uts_craftable.craftableStub.name == str_craftable_name )
2496  {
2497  player = GetPlayers()[0];
2498  player ‪player_finish_craftable( uts_craftable.craftableSpawn );
2499  thread ‪zm_unitrigger::unregister_unitrigger( uts_craftable );
2500 
2501  if ( isdefined( uts_craftable.craftableStub.onFullyCrafted ) )
2502  {
2503  uts_craftable [[ uts_craftable.craftableStub.onFullyCrafted ]]();
2504  }
2505 
2506  return;
2507  }
2508  }
2509 }
2510 
2511 
2513 {
2514  ArrayRemoveValue( level.a_uts_craftables, self );
2515 }
2516 
2518 {
2519  can_use = self.stub ‪craftablestub_update_prompt( player );
2520  self SetHintString( self.stub.hint_string );
2521  return can_use;
2522 }
2523 
2524 
2525 //
2526 // This determines what kind of hintstring the player will see when in the trigger.
2527 // Returns false if the player cannot use the trigger.
2528 // Returns true if it is usable.
2529 // self is a uts_craftable
2530 function ‪craftablestub_update_prompt( player, unitrigger, slot = self.craftableStub.inventory_slot )
2531 {
2532  ‪DEFAULT( player.current_craftable_pieces, [] );
2533 
2534  if ( !self ‪anystub_update_prompt( player ) )
2535  {
2536  return false;
2537  }
2538 
2539  if ( player ‪bgb::is_enabled( "zm_bgb_disorderly_combat" ) )
2540  {
2541  self.hint_string = "";
2542  return false;
2543  }
2544 
2545  if ( ‪IS_TRUE( self.is_locked ) )
2546  {
2547  return true;
2548  }
2549 
2550  can_use = true;
2551 
2552  if ( isdefined( self.custom_craftablestub_update_prompt ) && !self [[ self.custom_craftablestub_update_prompt ]]( player ) )
2553  {
2554  return false;
2555  }
2556 
2557  initial_current_weapon = player getCurrentWeapon();
2558  current_weapon = ‪zm_weapons::get_nonalternate_weapon( initial_current_weapon );
2559  if ( current_weapon.isheroweapon || current_weapon.isgadget )
2560  {
2561  self.hint_string = "";
2562  return false;
2563  }
2564 
2565  if ( !‪IS_TRUE( self.crafted ) )
2566  {
2567  // If they don't have any shared pieces that can be used OR
2568  // If they don't have a piece, tell them to get more
2569  if ( !self.craftableSpawn ‪craftable_can_use_shared_piece() )
2570  {
2571  if ( !isdefined( player.current_craftable_pieces[slot] ) )
2572  {
2573  self.hint_string = &"ZOMBIE_BUILD_PIECE_MORE";
2574  return false;
2575  }
2576  // If they have a piece but it can't be used here
2577  else if ( !self.craftableSpawn ‪craftable_has_piece( player.current_craftable_pieces[slot] ) )
2578  {
2579  self.hint_string = &"ZOMBIE_BUILD_PIECE_WRONG";
2580  return false;
2581  }
2582  }
2583 
2584  // Otherwise, it seems like they can craft here!
2585  assert ( isdefined( level.zombie_craftableStubs[ self.equipname ].str_to_craft ), "Missing craftable hint" );
2586 
2587  self.hint_string = level.zombie_craftableStubs[ self.equipname ].str_to_craft;
2588  }
2589  else if ( self.persistent == ‪PERSISTENT )
2590  {
2591  if ( ‪zm_equipment::is_limited( self.weaponname ) && ‪zm_equipment::limited_in_use( self.weaponname ) )
2592  {
2593  self.hint_string = &"ZOMBIE_BUILD_PIECE_ONLY_ONE";
2594  return false;
2595  }
2596 
2597  if ( player ‪zm_equipment::has_player_equipment( self.weaponname ) )
2598  {
2599  self.hint_string = &"ZOMBIE_BUILD_PIECE_HAVE_ONE";
2600  return false;
2601  }
2602 
2603  self.hint_string = self.trigger_hintstring;
2604  }
2605  else if ( self.persistent == ‪ONE_USE_AND_FLY )
2606  {
2607  if ( !‪zm_weapons::limited_weapon_below_quota( self.weaponname, undefined ) )
2608  {
2609  self.hint_string = &"ZOMBIE_GO_TO_THE_BOX_LIMITED";
2610  return false;
2611  }
2612  else if ( ‪IS_TRUE( self.str_taken ) )
2613  {
2614  self.hint_string = &"ZOMBIE_GO_TO_THE_BOX";
2615  return false;
2616  }
2617 
2618  self.hint_string = self.trigger_hintstring;
2619  }
2620  else
2621  {
2622  self.hint_string = "";
2623  return false;
2624  }
2625 
2626  return true;
2627 }
2628 
2629 
2630 //
2631 // Wait until the player chooses a craftable or leaves the trigger
2632 // self is a uts_craftable
2633 function ‪choose_open_craftable( player )
2634 {
2635  self endon( "kill_choose_open_craftable" );
2636 
2637  n_playernum = player GetEntityNumber();
2638  self.b_open_craftable_checking_input = true;
2639  b_got_input = true; // Trigger the hint string update
2640 
2641  // Display control hint text
2642  hintTextHudElem = newclientHudElem( player );
2643  hintTextHudElem.alignX = "center";
2644  hintTextHudElem.alignY = "middle";
2645  hintTextHudElem.horzAlign = "center";
2646  hintTextHudElem.vertAlign = "middle";
2647  hintTextHudElem.y = 95;
2648 
2649  if ( player IsSplitScreen() )
2650  {
2651  hintTextHudElem.y = -50;
2652  }
2653 
2654  hintTextHudElem.foreground = true;
2655  hintTextHudElem.font = "default";
2656  hintTextHudElem.fontScale = 1.1;
2657  hintTextHudElem.alpha = 1;
2658  hintTextHudElem.color = ( 1.0, 1.0, 1.0 );
2659  hintTextHudElem setText( &"ZOMBIE_CRAFTABLE_CHANGE_BUILD" );
2660 
2661  if ( !isdefined( self.openCraftableHudElem ) )
2662  {
2663  self.openCraftableHudElem = [];
2664  }
2665 
2666  self.openCraftableHudElem[ n_playernum ] = hintTextHudElem;
2667 
2668  // Loop while we're inside the trigger.
2669  // Also, we'll need to cut out if you crafted something.
2670  // If the craftable is persistent, then we'd still be looping here without this check
2671  while ( isdefined( self.playertrigger[ n_playernum ] ) && !self.crafted )
2672  {
2673  if ( player ActionSlotOneButtonPressed() )
2674  {
2675  self.n_open_craftable_choice++;
2676  b_got_input = true;
2677  }
2678  else if ( player ActionSlotTwoButtonPressed() )
2679  {
2680  self.n_open_craftable_choice--;
2681  b_got_input = true;
2682  }
2683 
2684  // Limit check. If we're out of bounds, wrap around
2685  // This list could change if someone else crafted or picked up something, so keep checking
2686  if ( self.n_open_craftable_choice >= self.a_uts_open_craftables_available.size )
2687  {
2688  self.n_open_craftable_choice = 0;
2689  }
2690  else if ( self.n_open_craftable_choice < 0 )
2691  {
2692  self.n_open_craftable_choice = self.a_uts_open_craftables_available.size - 1;
2693  }
2694 
2695  if ( b_got_input )
2696  {
2697  // Update the prompt...promptly!
2698  self.equipname = self.a_uts_open_craftables_available[ self.n_open_craftable_choice ].equipname;
2699  self.hint_string = self.a_uts_open_craftables_available[ self.n_open_craftable_choice ].hint_string;
2700  self.playertrigger[ n_playernum ] SetHintString( self.hint_string );
2701  b_got_input = false;
2702  wait 0.5; // Don't let the input cycle too fast
2703  }
2704 
2705  // Visibility check
2706  if ( player ‪util::is_player_looking_at( self.playertrigger[ n_playernum ].origin, ‪ZM_CRAFTABLES_LOOKAT_DOT ) )
2707  {
2708  self.openCraftableHudElem[ n_playernum ].alpha = 1;
2709  }
2710  else
2711  {
2712  self.openCraftableHudElem[ n_playernum ].alpha = 0;
2713  }
2714 
2716  }
2717 
2718  self.b_open_craftable_checking_input = false;
2719  self.openCraftableHudElem[ n_playernum ] ‪destroy();
2720  self.openCraftableHudElem[ n_playernum ] = undefined;
2721 }
2722 
2723 
2724 //
2725 // This determines what kind of hintstring the player will see when in the trigger.
2726 // NOTE: This function is called once a second. It's also called when
2727 // you're trying to craft something, so the logic makes my head hurt.
2728 // self is a unitrigger craftable stub
2729 // Returns false if the player cannot use the trigger.
2730 // Returns true if it is usable.
2731 function ‪open_craftablestub_update_prompt( player, slot = 0 )
2732 {
2733  // If we haven't already built something here
2734  if ( !‪IS_TRUE( self.crafted ) )
2735  {
2736  // Check each craftable to see if we can try to make it here
2737  self.a_uts_open_craftables_available = []; // have we found a craftable that can be used here?
2738 
2739  foreach ( uts_craftable in level.a_uts_craftables )
2740  {
2741  // If our craftable is the open type AND
2742  // it hasn't already been crafted AND
2743  // it's not really an open_table trigger AND
2744  // they have any shared pieces that can be used
2745  if ( ‪IS_TRUE( uts_craftable.craftableStub.is_open_table ) &&
2746  !‪IS_TRUE( uts_craftable.crafted ) &&
2747  uts_craftable.craftableSpawn.craftable_name != "open_table" &&
2748  uts_craftable.craftableSpawn ‪craftable_can_use_shared_piece() )
2749  {
2750  // we could potentially craft this thing
2751  self.a_uts_open_craftables_available[self.a_uts_open_craftables_available.size] = uts_craftable;
2752  }
2753  }
2754 
2755  // So how many candidates did we find?
2756 
2757  // If less than 2 kill any existing input checking funcs
2758  if ( self.a_uts_open_craftables_available.size < 2 )
2759  {
2760  self notify( "kill_choose_open_craftable" );
2761  self.b_open_craftable_checking_input = false;
2762  n_entitynum = player GetEntityNumber();
2763 
2764  if ( isdefined( self.openCraftableHudElem ) &&
2765  isdefined( self.openCraftableHudElem[ n_entitynum ] ) )
2766  {
2767  self.openCraftableHudElem[ n_entitynum ] ‪destroy();
2768  self.openCraftableHudElem[ n_entitynum ] = undefined;
2769  }
2770  }
2771 
2772  switch ( self.a_uts_open_craftables_available.size )
2773  {
2774  case 0:
2775  if ( !isdefined( player.current_craftable_pieces[slot] ) )
2776  {
2777  self.hint_string = &"ZOMBIE_BUILD_PIECE_MORE";
2778  self.n_open_craftable_choice = ‪CRAFTABLE_INVALID_CHOICE;
2779  return false;
2780  }
2781 
2782  break;
2783 
2784  case 1:
2785  // Otherwise, it seems like they can craft here!
2786  self.n_open_craftable_choice = 0;
2787  self.equipname = self.a_uts_open_craftables_available[ self.n_open_craftable_choice ].equipname;
2788  return true;
2789 
2790  default:
2791 
2792  // If we have 2 or more, we need to show the player a way to choose between them.
2793  //TODO Known bug: If more than one person approaches the same table, only the first
2794  // person will be able to choose. How do we want to handle this?
2795  if ( !self.b_open_craftable_checking_input )
2796  {
2797  thread ‪choose_open_craftable( player );
2798  }
2799 
2800  return true;
2801  }
2802  }
2803  else if ( self.persistent == ‪ONE_USE_AND_FLY )
2804  {
2805 
2806  if ( !‪zm_weapons::limited_weapon_below_quota( self.weaponname, undefined ) )
2807  {
2808  self.hint_string = &"ZOMBIE_GO_TO_THE_BOX_LIMITED";
2809  return false;
2810  }
2811  else if ( ‪IS_TRUE( self.bought ) )
2812  {
2813  self.hint_string = &"ZOMBIE_GO_TO_THE_BOX";
2814  return false;
2815  }
2816  else if ( ‪IS_TRUE( self.str_taken ) )
2817  {
2818  self.hint_string = &"ZOMBIE_GO_TO_THE_BOX";
2819  return false;
2820  }
2821 
2822  self.hint_string = self.trigger_hintstring;
2823  return true;
2824  }
2825  else if ( self.persistent == ‪PERSISTENT )
2826  {
2827  return true;
2828  }
2829 
2830  return false;
2831 }
2832 
2833 
2834 //
2835 // self is a player
2836 function ‪player_continue_crafting( craftableSpawn, slot )
2837 {
2839  {
2840  return false;
2841  }
2842 
2843 // if( self isThrowingGrenade() )
2844 // return false;
2845  if ( !( self ‪player_can_craft( craftableSpawn, true ) ) )
2846  {
2847  return false;
2848  }
2849 
2850  if ( isdefined( self.screecher ) )
2851  {
2852  return false;
2853  }
2854 
2855  if ( !self UseButtonPressed() )
2856  {
2857  return false;
2858  }
2859 
2860  if ( craftableSpawn.stub.useTime > 0 && isdefined( slot ) && !craftableSpawn ‪craftable_is_piece_crafting( self.current_craftable_pieces[slot] ) )
2861  {
2862  return false;
2863  }
2864 
2865  trigger = craftableSpawn.stub ‪zm_unitrigger::unitrigger_trigger( self );
2866 
2867  if ( craftableSpawn.stub.script_unitrigger_type == "unitrigger_radius_use" )
2868  {
2869  torigin = craftableSpawn.stub ‪zm_unitrigger::unitrigger_origin();
2870  porigin = self GetEye();
2871  radius_sq = ( 1.5 * 1.5 ) * craftableSpawn.stub.radius * craftableSpawn.stub.radius;
2872 
2873  ‪if ( Distance2DSquared( torigin, porigin ) > radius_sq )
2874  {
2875  return false;
2876  }
2877  }
2878  else
2879  {
2880  if ( !isdefined( trigger ) || !trigger IsTouching( self ) ) //self IsTouching(trigger))
2881  {
2882  return false;
2883  }
2884  }
2885 
2886  if ( ‪IS_TRUE( craftableSpawn.stub.require_look_at ) && !self ‪util::is_player_looking_at( trigger.origin, ‪ZM_CRAFTABLES_LOOKAT_DOT ) )
2887  {
2888  return false;
2889  }
2890 
2891  return true;
2892 }
2893 
2894 function ‪player_progress_bar_update( start_time, craft_time )
2895 {
2896  self endon( "entering_last_stand" );
2897  self endon( "death" );
2898  self endon( "disconnect" );
2899  self endon( "craftable_progress_end" );
2900 
2901  while ( isdefined( self ) && getTime() - start_time < craft_time )
2902  {
2903  progress = ( getTime() - start_time ) / craft_time;
2904 
2905  if ( progress < 0 )
2906  {
2907  progress = 0;
2908  }
2909 
2910  if ( progress > 1 )
2911  {
2912  progress = 1;
2913  }
2914 
2915  self.useBar ‪hud::updateBar( progress );
2917  }
2918 
2919 }
2920 
2921 function ‪player_progress_bar( start_time, craft_time )
2922 {
2923  self.useBar = self ‪hud::createPrimaryProgressBar();
2924  self.useBarText = self ‪hud::createPrimaryProgressBarText();
2925  self.useBarText setText( &"ZOMBIE_BUILDING" );
2926 
2927  if ( isdefined( self ) && isdefined( start_time ) && isdefined( craft_time ) )
2928  {
2929  self ‪player_progress_bar_update( start_time, craft_time );
2930  }
2931 
2932  self.useBarText ‪hud::destroyElem();
2933  self.useBar ‪hud::destroyElem();
2934 }
2935 
2936 
2937 //
2938 // self is a unitrigger
2939 function ‪craftable_use_hold_think_internal( player, slot = self.stub.craftableSpawn.inventory_slot )
2940 {
2941  wait 0.01;
2942 
2943  if ( !isdefined( self ) )
2944  {
2945  //make sure the audio sounds go away
2946  if ( isdefined( player.craftableAudio ) )
2947  {
2948  player.craftableAudio delete();
2949  player.craftableAudio = undefined;
2950  }
2951 
2952  return;
2953  }
2954 
2955  if ( self.stub.craftableSpawn ‪craftable_can_use_shared_piece() )
2956  {
2957  slot = undefined;
2958  }
2959 
2960  if ( !isdefined( self.useTime ) )
2961  {
2962  self.useTime = int( 3 * 1000 );
2963  }
2964 
2965  self.craft_time = self.useTime;
2966  self.craft_start_time = getTime();
2967  craft_time = self.craft_time;
2968  craft_start_time = self.craft_start_time;
2969 
2970  if ( craft_time > 0 )
2971  {
2973 
2975  orgweapon = player GetCurrentWeapon();
2976  build_weapon = GetWeapon( "zombie_builder" );
2977  player GiveWeapon( build_weapon );
2978  player SwitchToWeapon( build_weapon );
2979 
2980  if ( isdefined( slot ) )
2981  {
2982  self.stub.craftableSpawn ‪craftable_set_piece_crafting( player.current_craftable_pieces[slot] );
2983  }
2984  else
2985  {
2987  }
2988 
2989  player thread ‪player_progress_bar( craft_start_time, craft_time );
2990 
2991  //check to see if this is the final piece
2992  if ( isdefined( level.craftable_craft_custom_func ) )
2993  {
2994  player thread [[level.craftable_craft_custom_func]]( self.stub );
2995  }
2996 
2997 
2998  while ( isdefined( self ) && player ‪player_continue_crafting( self.stub.craftableSpawn, slot ) && getTime() - self.craft_start_time < self.craft_time )
2999  {
3001  }
3002 
3003  player notify( "craftable_progress_end" );
3004 
3005  player ‪zm_weapons::switch_back_primary_weapon( orgweapon );
3006  //player SwitchToWeapon( orgweapon );
3007  player TakeWeapon( build_weapon );
3008 
3009  if ( ‪IS_TRUE( player.is_drinking ) )
3010  {
3012  }
3013 
3015  }
3016 
3017  if ( isdefined( self ) &&
3018  player ‪player_continue_crafting( self.stub.craftableSpawn, slot ) &&
3019  ( self.craft_time <= 0 || getTime() - self.craft_start_time >= self.craft_time ) )
3020  {
3021  if ( isdefined( slot ) )
3022  {
3023  self.stub.craftableSpawn ‪craftable_clear_piece_crafting( player.current_craftable_pieces[slot] );
3024  }
3025  else
3026  {
3028  }
3029 
3030  self notify( "craft_succeed" );
3031  }
3032  else
3033  {
3034  //make sure the audio sounds go away
3035  if ( isdefined( player.craftableAudio ) )
3036  {
3037  player.craftableAudio delete();
3038  player.craftableAudio = undefined;
3039  }
3040 
3041  if ( isdefined( slot ) )
3042  {
3043  self.stub.craftableSpawn ‪craftable_clear_piece_crafting( player.current_craftable_pieces[slot] );
3044  }
3045  else
3046  {
3048  }
3049 
3050  self notify( "craft_failed" );
3051  }
3052 
3053 }
3054 
3055 function ‪craftable_play_craft_fx( player )
3056 {
3057  self endon( "kill_trigger" );
3058  self endon( "craft_succeed" );
3059  self endon( "craft_failed" );
3060 
3061  while ( 1 )
3062  {
3063  PlayFX( level._effect["building_dust"], player GetPlayerCameraPos(), player.angles );
3064  //PlayFxOnTag( level._effect["crafting_dust"], player, "tag_camera" );
3065  wait 0.5;
3066  }
3067 }
3068 
3070 {
3071  self thread ‪craftable_play_craft_fx( player );
3072  self thread ‪craftable_use_hold_think_internal( player );
3073  retval = self ‪util::waittill_any_return( "craft_succeed", "craft_failed" );
3074 
3075  if ( retval == "craft_succeed" )
3076  {
3077  return true;
3078  }
3079 
3080  return false;
3081 }
3082 
3083 
3084 // Main unitrigger think function for craftable place triggers
3085 // self is a unitrigger
3087 {
3088  self notify( "craftable_place_think" );
3089  self endon( "craftable_place_think" );
3090  self endon( "kill_trigger" );
3091 
3092  player_crafted = undefined;
3093 
3094  while ( !‪IS_TRUE( self.stub.crafted ) )
3095  {
3096  self waittill( "trigger", player );
3097 
3098  if ( isdefined( level.custom_craftable_validation ) )
3099  {
3100  valid = self [[ level.custom_craftable_validation ]]( player );
3101 
3102  if ( !valid )
3103  {
3104  continue;
3105  }
3106  }
3107 
3108  if ( player != self.parent_player )
3109  {
3110  continue;
3111  }
3112 
3113  if ( isdefined( player.screecher_weapon ) )
3114  {
3115  continue;
3116  }
3117 
3118  if ( !‪zm_utility::is_player_valid( player ) )
3119  {
3120  player thread ‪zm_utility::ignore_triggers( 0.5 );
3121  continue;
3122  }
3123 
3124  status = player ‪player_can_craft( self.stub.craftableSpawn, false );
3125 
3126  if ( !status )
3127  {
3128  self.stub.hint_string = "";
3129  self SetHintString( self.stub.hint_string );
3130 
3131  if ( isdefined( self.stub.onCantUse ) )
3132  {
3133  self.stub [[ self.stub.onCantUse ]]( player );
3134  }
3135  }
3136  else
3137  {
3138  if ( isdefined( self.stub.onBeginUse ) )
3139  {
3140  self.stub [[ self.stub.onBeginUse ]]( player );
3141  }
3142 
3143  ‪result = self ‪craftable_use_hold_think( player );
3144  team = player.pers["team"];
3145 
3146  if ( isdefined( self.stub.onEndUse ) )
3147  {
3148  self.stub [[ self.stub.onEndUse ]]( team, player, ‪result );
3149  }
3150 
3151  if ( !‪result )
3152  {
3153  continue;
3154  }
3155 
3156  if ( isdefined( self.stub.onUse ) )
3157  {
3158  self.stub [[ self.stub.onUse ]]( player );
3159  }
3160 
3161  prompt = player ‪player_craft( self.stub.craftableSpawn );
3162  player_crafted = player;
3163  self.stub.hint_string = prompt;
3164  self SetHintString( self.stub.hint_string );
3165  }
3166  }
3167 
3168  // Run a custom function if called for.
3169  // If it returns false, then stop processing.
3170  if ( isdefined( self.stub.craftableStub.onFullyCrafted ) )
3171  {
3172  b_result = self.stub [[ self.stub.craftableStub.onFullyCrafted ]]();
3173 
3174  if ( !b_result )
3175  {
3176  return;
3177  }
3178  }
3179 
3180  if ( isdefined( player_crafted ) )
3181  {
3182  player_crafted playsound( "zmb_craftable_complete" );
3183  }
3184 
3185  if ( self.stub.persistent == ‪ONE_TIME_CRAFT )
3186  {
3187  self.stub ‪craftablestub_remove();
3188  thread ‪zm_unitrigger::unregister_unitrigger( self.stub );
3189  return;
3190  }
3191 
3192  if ( self.stub.persistent == ‪UNCRAFT )
3193  {
3194  ‪stub_uncraft_craftable( self.stub, true );
3195  return;
3196  }
3197 
3198  if ( self.stub.persistent == ‪ONE_USE_AND_FLY )
3199  {
3200  if ( isdefined( player_crafted ) )
3201  {
3202  self ‪craftabletrigger_update_prompt( player_crafted );
3203  }
3204 
3205  if ( !‪zm_weapons::limited_weapon_below_quota( self.stub.weaponname, undefined ) )
3206  {
3207  self.stub.hint_string = &"ZOMBIE_GO_TO_THE_BOX_LIMITED";
3208  self SetHintString( self.stub.hint_string );
3209  return;
3210  }
3211 
3212  if ( ‪IS_TRUE( self.stub.str_taken ) )
3213  {
3214  self.stub.hint_string = &"ZOMBIE_GO_TO_THE_BOX";
3215  self SetHintString( self.stub.hint_string );
3216  return;
3217  }
3218 
3219  if ( isdefined( self.stub.model ) )
3220  {
3221  self.stub.model NotSolid();
3222  self.stub.model show();
3223  }
3224 
3225  while ( self.stub.persistent == ‪ONE_USE_AND_FLY )
3226  {
3227  self waittill( "trigger", player );
3228 
3229  if( isdefined(self.stub.bought) && self.stub.bought == 1 )
3230  {
3231  continue; //one time use
3232  }
3233 
3234  if ( isdefined( player.screecher_weapon ) )
3235  {
3236  continue;
3237  }
3238 
3239  current_weapon = player GetCurrentWeapon();
3241  {
3242  continue;
3243  }
3244 
3245  if ( current_weapon.isheroweapon || current_weapon.isgadget )
3246  {
3247  continue;
3248  }
3249 
3250  if ( player ‪bgb::is_enabled( "zm_bgb_disorderly_combat" ) )
3251  {
3252  continue; // PORTIZ 7/22/16: player can't pick up weapons during Disorderly Combat. had to move here instead of visibility because the visibility function is
3253  } // shared with the individual craftable pieces...
3254 
3255  if ( isdefined( level.custom_craftable_validation ) )
3256  {
3257  valid = self [[ level.custom_craftable_validation ]]( player );
3258 
3259  if ( !valid )
3260  {
3261  continue;
3262  }
3263  }
3264 
3265  if ( !‪IS_TRUE( self.stub.crafted ) )
3266  {
3267  self.stub.hint_string = "";
3268  self SetHintString( self.stub.hint_string );
3269  return;
3270  }
3271 
3272  if ( player != self.parent_player )
3273  {
3274  continue;
3275  }
3276 
3277  if ( !‪zm_utility::is_player_valid( player ) )
3278  {
3279  player thread ‪zm_utility::ignore_triggers( 0.5 );
3280  continue;
3281  }
3282 
3283  self.stub.bought = 1;
3284 
3285  if ( isdefined( self.stub.model ) )
3286  {
3287  self.stub.model thread ‪model_fly_away( self );
3288  }
3289 
3290  if ( ‪zm_weapons::limited_weapon_below_quota( self.stub.weaponname, undefined ) )
3291  {
3292  player ‪zm_weapons::weapon_give( self.stub.weaponname );
3293 
3294  if ( isdefined( level.zombie_include_craftables[ self.stub.equipname ].onBuyWeapon ) )
3295  {
3296  self [[level.zombie_include_craftables[ self.stub.equipname ].onBuyWeapon]]( player );
3297  }
3298  }
3299 
3300  if ( !‪zm_weapons::limited_weapon_below_quota( self.stub.weaponname, undefined ) )
3301  {
3302  self.stub.hint_string = &"ZOMBIE_GO_TO_THE_BOX_LIMITED";
3303  }
3304  else
3305  {
3306  self.stub.hint_string = &"ZOMBIE_GO_TO_THE_BOX";
3307  }
3308 
3309  self SetHintString( self.stub.hint_string );
3310 
3311  //stat tracking
3312  player ‪track_craftables_pickedup( self.stub.craftableSpawn );
3313 
3314  }
3315  }
3316  else if ( !isdefined( player_crafted ) || self ‪craftabletrigger_update_prompt( player_crafted ) )
3317  {
3318  visible = true;
3319  hide = ‪get_hide_model_if_unavailable( self.stub.equipname );
3320  if ( hide && isdefined( level.custom_craftable_validation ) )
3321  {
3322  visible = self [[ level.custom_craftable_validation ]]( player );
3323  }
3324 
3325  if ( visible && isdefined( self.stub.model ) )
3326  {
3327  self.stub.model NotSolid();
3328  self.stub.model show();
3329  }
3330 
3331  while ( self.stub.persistent == ‪PERSISTENT )
3332  {
3333  self waittill( "trigger", player );
3334 
3335  if ( isdefined( player.screecher_weapon ) )
3336  {
3337  continue;
3338  }
3339 
3340  if ( isdefined( level.custom_craftable_validation ) )
3341  {
3342  valid = self [[ level.custom_craftable_validation ]]( player );
3343 
3344  if ( !valid )
3345  {
3346  continue;
3347  }
3348  }
3349 
3350  if ( !‪IS_TRUE( self.stub.crafted ) )
3351  {
3352  self.stub.hint_string = "";
3353  self SetHintString( self.stub.hint_string );
3354  return;
3355  }
3356 
3357  if ( player != self.parent_player )
3358  {
3359  continue;
3360  }
3361 
3362  if ( !‪zm_utility::is_player_valid( player ) )
3363  {
3364  player thread ‪zm_utility::ignore_triggers( 0.5 );
3365  continue;
3366  }
3367 
3368  if ( player ‪zm_equipment::has_player_equipment( self.stub.weaponname ) )
3369  {
3370  continue;
3371  }
3372 
3373  if ( player ‪bgb::is_enabled( "zm_bgb_disorderly_combat" ) )
3374  {
3375  continue; // PORTIZ 7/22/16: player can't pick up weapons during Disorderly Combat. had to move here instead of visibility because the visibility function is
3376  } // shared with the individual craftable pieces...
3377 
3378  if ( isdefined( level.zombie_craftable_persistent_weapon ) )
3379  {
3380  if ( self [[level.zombie_craftable_persistent_weapon]]( player ) )
3381  {
3382  continue;
3383  }
3384  }
3385 
3386  if ( isdefined( level.zombie_custom_equipment_setup ) )
3387  {
3388  if ( self [[level.zombie_custom_equipment_setup]]( player ) )
3389  {
3390  continue;
3391  }
3392  }
3393 
3394  if ( !‪zm_equipment::is_limited( self.stub.weaponname ) || !‪zm_equipment::limited_in_use( self.stub.weaponname ) )
3395  {
3396  player ‪zm_equipment::buy( self.stub.weaponname );
3397  player GiveWeapon( self.stub.weaponname );
3398  player ‪zm_equipment::start_ammo( self.stub.weaponname );
3399  player notify( self.stub.weaponname.name + "_pickup_from_table" ); // 12-2-2015 BO3 FFOTD fix - notify player when picking up weapon from table, so we can ensure that players get upgraded or special versions in level-specific script
3400 
3401  if ( isdefined( level.zombie_include_craftables[ self.stub.equipname ].onBuyWeapon ) )
3402  {
3403  self [[level.zombie_include_craftables[ self.stub.equipname ].onBuyWeapon]]( player );
3404  }
3405 
3406  else if ( self.stub.weaponname != "keys_zm" )
3407  {
3408  player setactionslot( 1, "weapon", self.stub.weaponname );
3409  }
3410 
3411  if ( isdefined( level.zombie_craftableStubs[ self.stub.equipname ].str_taken ) )
3412  {
3413  self.stub.hint_string = level.zombie_craftableStubs[ self.stub.equipname ].str_taken;
3414  }
3415  else
3416  {
3417  self.stub.hint_string = "";
3418  }
3419 
3420  self SetHintString( self.stub.hint_string );
3421 
3422  //stat tracking
3423  player ‪track_craftables_pickedup( self.stub.craftableSpawn );
3424  }
3425  else
3426  {
3427  self.stub.hint_string = "";
3428  self SetHintString( self.stub.hint_string );
3429  }
3430  }
3431  }
3432 }
3433 
3434 function ‪model_fly_away( unitrigger )
3435 {
3436  self moveto( self.origin + ( 0, 0, 40 ), 3 );
3437  direction = self.origin;
3438  direction = ( direction[1], direction[0], 0 );
3439 
3440  if ( direction[1] < 0 || ( direction[0] > 0 && direction[1] > 0 ) )
3441  {
3442  direction = ( direction[0], direction[1] * -1, 0 );
3443  }
3444  else if ( direction[0] < 0 )
3445  {
3446  direction = ( direction[0] * -1, direction[1], 0 );
3447  }
3448 
3449  self Vibrate( direction, 10, 0.5, 4 );
3450  self waittill( "movedone" );
3451 
3452  self Ghost();
3453 
3454  PlayFX( level._effect["poltergeist"], self.origin );
3455 }
3456 
3457 
3458 function ‪find_craftable_stub( equipname )
3459 {
3460  foreach ( stub in level.a_uts_craftables )
3461  {
3462  if ( stub.equipname == equipname )
3463  {
3464  return stub;
3465  }
3466  }
3467 
3468  return undefined;
3469 }
3470 
3471 function ‪uncraft_craftable( equipname, return_pieces, origin, angles )
3472 {
3473  stub = ‪find_craftable_stub( equipname );
3474 
3475  ‪stub_uncraft_craftable( stub, return_pieces, origin, angles );
3476 }
3477 
3478 function ‪stub_uncraft_craftable( stub, return_pieces, origin, angles, use_random_start )
3479 {
3480  if ( isdefined( stub ) )
3481  {
3482  craftable = stub.craftableSpawn;
3483  craftable.crafted = false;
3484  craftable.stub.crafted = false;
3485  craftable notify( "uncrafted" );
3486  level.craftables_crafted[ craftable.craftable_name ] = false;
3487  level notify( craftable.craftable_name + "_uncrafted" );
3488 
3489  for ( i = 0; i < craftable.a_pieceSpawns.size; i++ )
3490  {
3491  craftable.a_pieceSpawns[i].crafted = false;
3492 
3493  if ( isdefined( craftable.a_pieceSpawns[i].tag_name ) )
3494  {
3495  craftable.stub.model NotSolid();
3496 
3497  if ( !‪IS_TRUE( craftable.a_pieceSpawns[i].crafted ) )
3498  {
3499  craftable.stub.model HidePart( craftable.a_pieceSpawns[i].tag_name );
3500  }
3501  else
3502  {
3503  craftable.stub.model show();
3504  craftable.stub.model ShowPart( craftable.a_pieceSpawns[i].tag_name );
3505  }
3506  }
3507 
3508  if ( ‪IS_TRUE( return_pieces ) )
3509  {
3510  craftable.a_pieceSpawns[i] ‪piece_spawn_at( origin, angles, use_random_start );
3511  }
3512  }
3513 
3514  if ( isdefined( craftable.stub.model ) )
3515  {
3516  craftable.stub.model Ghost();
3517  }
3518  }
3519 }
3520 
3521 function ‪player_explode_craftable( equipname, origin, speed, return_to_spawn, return_time )
3522 {
3523  self ‪ExplosionDamage( 50, origin );
3524 
3525  stub = ‪find_craftable_stub( equipname );
3526 
3527  if ( isdefined( stub ) )
3528  {
3529  craftable = stub.craftableSpawn;
3530  craftable.crafted = false;
3531  craftable.stub.crafted = false;
3532  craftable notify( "uncrafted" );
3533  level.craftables_crafted[ craftable.craftable_name ] = false;
3534  level notify( craftable.craftable_name + "_uncrafted" );
3535 
3536  for ( i = 0; i < craftable.a_pieceSpawns.size; i++ )
3537  {
3538  craftable.a_pieceSpawns[i].crafted = false;
3539 
3540  if ( isdefined( craftable.a_pieceSpawns[i].tag_name ) )
3541  {
3542  craftable.stub.model NotSolid();
3543 
3544  if ( !‪IS_TRUE( craftable.a_pieceSpawns[i].crafted ) )
3545  {
3546  craftable.stub.model HidePart( craftable.a_pieceSpawns[i].tag_name );
3547  }
3548  else
3549  {
3550  craftable.stub.model show();
3551  craftable.stub.model ShowPart( craftable.a_pieceSpawns[i].tag_name );
3552  }
3553  }
3554 
3555  ang = RandomFloat( 360 );
3556  h = 0.25 + RandomFloat( 0.5 );
3557  dir = ( Sin( ang ), Cos( ang ), h );
3558  self thread ‪player_throw_piece( craftable.a_pieceSpawns[i], origin, speed * dir, return_to_spawn, return_time );
3559  }
3560 
3561  craftable.stub.model Ghost();
3562  }
3563 }
3564 
3565 
3566 //
3567 //
3569 {
3570  foreach ( craftable in level.zombie_include_craftables )
3571  {
3572  if ( isdefined( craftable.triggerThink ) )
3573  {
3574  craftable [[craftable.triggerThink]]();
3575  }
3576  }
3577 }
3578 
3579 
3580 // Add open table craftable triggers
3582 {
3583  a_trigs = GetEntArray( "open_craftable_trigger", "targetname" );
3584 
3585  foreach ( trig in a_trigs )
3586  {
3587  unitrigger_stub = ‪setup_unitrigger_craftable_internal( trig, "open_table", "", "OPEN_CRAFTABLE", ‪DELETE_TRIGGER, ‪ONE_TIME_CRAFT );
3588  // use look-toward instead of look-at for the tables
3589  unitrigger_stub.require_look_at = false;
3590  unitrigger_stub.require_look_toward = true;
3591  }
3592 }
3593 
3594 
3595 // self is craftable data (CD)
3596 function ‪craftable_trigger_think( trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
3597 {
3598  return ‪setup_unitrigger_craftable( trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent );
3599 }
3600 
3601 function ‪craftable_trigger_think_array( trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
3602 {
3603  return ‪setup_unitrigger_craftable_array( trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent );
3604 }
3605 
3606 
3607 
3608 //
3609 // delete_trigger should probably be KEEP_TRIGGER since the dynamic unitriggers
3610 // rely on the .origin_parent's origin to determine the trigger's location
3611 function ‪setup_vehicle_unitrigger_craftable( parent, trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
3612 {
3613  trig = GetEnt( trigger_targetname, "targetname" );
3614 
3615  if ( !isdefined( trig ) )
3616  {
3617  return;
3618  }
3619 
3620  unitrigger_stub = spawnstruct();
3621 
3622  unitrigger_stub.craftableStub = level.zombie_include_craftables[ equipname ];
3623 
3624  unitrigger_stub.link_parent = parent;
3625  unitrigger_stub.origin_parent = trig;
3626  unitrigger_stub.trigger_targetname = trigger_targetname;
3627  unitrigger_stub.originFunc = &‪anystub_get_unitrigger_origin;
3628  unitrigger_stub.onSpawnFunc = &‪anystub_on_spawn_trigger;
3629  unitrigger_stub.origin = trig.origin;
3630  unitrigger_stub.angles = trig.angles;
3631  unitrigger_stub.equipname = equipname;
3632  unitrigger_stub.weaponname = weaponname;
3633  unitrigger_stub.trigger_hintstring = trigger_hintstring;
3634  unitrigger_stub.delete_trigger = delete_trigger;
3635  unitrigger_stub.crafted = false;
3636  unitrigger_stub.persistent = persistent;
3637  unitrigger_stub.useTime = int( 3 * 1000 );
3638 
3639  unitrigger_stub.onBeginUse = &‪onBeginUseUTS;
3640  unitrigger_stub.onEndUse = &‪onEndUseUTS;
3641  unitrigger_stub.onUse = &‪onUsePlantObjectUTS;
3642  unitrigger_stub.onCantUse = &‪onCantUseUTS;
3643 
3644  tmins = trig GetMins();
3645  tmaxs = trig GetMaxs();
3646  tsize = tmaxs-tmins;
3647 
3648  if ( isdefined( trig.script_length ) )
3649  {
3650  unitrigger_stub.script_length = trig.script_length;
3651  }
3652  else
3653  {
3654  unitrigger_stub.script_length = tsize[1];
3655  }
3656 
3657  if ( isdefined( trig.script_width ) )
3658  {
3659  unitrigger_stub.script_width = trig.script_width;
3660  }
3661  else
3662  {
3663  unitrigger_stub.script_width = tsize[0];
3664  }
3665 
3666  if ( isdefined( trig.script_height ) )
3667  {
3668  unitrigger_stub.script_height = trig.script_height;
3669  }
3670  else
3671  {
3672  unitrigger_stub.script_height = tsize[2];
3673  }
3674 
3675  if ( isdefined( trig.radius ) )
3676  {
3677  unitrigger_stub.radius = trig.radius;
3678  }
3679  else
3680  {
3681  unitrigger_stub.radius = 64;
3682  }
3683 
3684  unitrigger_stub.target = trig.target;
3685  unitrigger_stub.targetname = trig.targetname + "_trigger";
3686 
3687  unitrigger_stub.script_noteworthy = trig.script_noteworthy;
3688  unitrigger_stub.script_parameters = trig.script_parameters;
3689 
3690  unitrigger_stub.cursor_hint = "HINT_NOICON";
3691 
3692  if ( isdefined( level.zombie_craftableStubs[ equipname ].str_to_craft ) )
3693  {
3694  unitrigger_stub.hint_string = level.zombie_craftableStubs[ equipname ].str_to_craft;
3695  }
3696 
3697  unitrigger_stub.script_unitrigger_type = "unitrigger_radius_use";
3698  unitrigger_stub.require_look_at = true;
3699 
3701  unitrigger_stub.prompt_and_visibility_func = &‪craftabletrigger_update_prompt;
3702 
3703  //zm_unitrigger::register_static_unitrigger(unitrigger_stub, &craftable_spawn_think);
3705 
3706  unitrigger_stub.piece_trigger = trig;
3707  trig.trigger_stub = unitrigger_stub;
3708 
3709  // create craftable pieces
3710  //unitrigger_stub.craftableSpawn = trig craftable_piece_triggers( equipname, unitrigger_stub.origin );
3711  unitrigger_stub.craftableSpawn = unitrigger_stub ‪craftable_piece_unitriggers( equipname, unitrigger_stub.origin );
3712 
3713  if ( delete_trigger )
3714  {
3715  trig delete();
3716  }
3717 
3718  level.a_uts_craftables[level.a_uts_craftables.size] = unitrigger_stub;
3719  return unitrigger_stub;
3720 }
3721 
3722 
3723 //
3724 // delete_trigger should probably be KEEP_TRIGGER since the dynamic unitriggers
3725 // rely on the .origin_parent's origin to determine the trigger's location
3726 // Also, this function is redundant because it just calls another function with the
3727 // same arguments. I'm only leaving it for compatibility reasons (stay similar to buildables).
3728 function ‪vehicle_craftable_trigger_think( vehicle, trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent )
3729 {
3730  return ‪setup_vehicle_unitrigger_craftable( vehicle, trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent );
3731 }
3732 
3733 
3734 
3735 function ‪onPickupUTS( player )
3736 {
3737 }
3738 
3739 function ‪onDropUTS( player )
3740 {
3741  player notify( "event_ended" );
3742 }
3743 
3744 function ‪onBeginUseUTS( player )
3745 {
3746  if ( isdefined( self.craftableStub.onBeginUse ) )
3747  {
3748  self [[ self.craftableStub.onBeginUse ]]( player );
3749  }
3750 
3751  if ( isdefined( player ) && !isdefined( player.craftableAudio ) )
3752  {
3753  player.craftableAudio = ‪spawn( "script_origin", player.origin );
3754  player.craftableAudio PlayLoopSound( "zmb_craftable_loop" );
3755  }
3756 }
3757 
3758 function ‪onEndUseUTS( team, player, ‪result )
3759 {
3760  if ( !isdefined( player ) )
3761  {
3762  return;
3763  }
3764 
3765  if ( isdefined( player.craftableAudio ) )
3766  {
3767  player.craftableAudio delete();
3768  player.craftableAudio = undefined;
3769  }
3770 
3771  if ( isdefined( self.craftableStub.onEndUse ) )
3772  {
3773  self [[ self.craftableStub.onEndUse ]]( team, player, ‪result );
3774  }
3775 
3776  player notify( "event_ended" );
3777 }
3778 
3779 function ‪onCantUseUTS( player )
3780 {
3781  if ( isdefined( self.craftableStub.onCantUse ) )
3782  {
3783  self [[ self.craftableStub.onCantUse ]]( player );
3784  }
3785 }
3786 
3787 function ‪onUsePlantObjectUTS( player )
3788 {
3789  if ( isdefined( self.craftableStub.onUsePlantObject ) )
3790  {
3791  self [[ self.craftableStub.onUsePlantObject ]]( player );
3792  }
3793 
3794  //* player playSound( "mpl_sd_bomb_plant" );
3795  player notify ( "bomb_planted" );
3796 
3797 }
3798 
3800 {
3801  // No Craftables Set Up In Map
3802  //----------------------------
3803  if ( !isdefined( level.zombie_craftableStubs ) )
3804  {
3805  return false;
3806  }
3807 
3808  // WallBuys
3809  //---------
3810  if ( isdefined( self.zombie_weapon_upgrade ) && isdefined( level.zombie_craftableStubs[ self.zombie_weapon_upgrade ] ) )
3811  {
3812  return true;
3813  }
3814 
3815  // Machines
3816  //---------
3817  if ( isdefined( self.script_noteworthy ) && self.script_noteworthy == "specialty_weapupgrade" )
3818  {
3819  if ( ‪IS_TRUE( level.craftables_crafted[ "pap" ] ) )
3820  {
3821  return false;
3822  }
3823 
3824  return true;
3825  }
3826 
3827  return false;
3828 }
3829 
3831 {
3832  self.a_pieceSpawns--;
3833 }
3834 
3836 {
3837  if ( self.a_pieceSpawns <= 0 )
3838  {
3839  return true;
3840  }
3841 
3842  return false;
3843 }
3844 
3845 
3846 function ‪get_craftable_hint( craftable_name )
3847 {
3848  assert( isdefined( level.zombie_craftableStubs[ craftable_name ] ), craftable_name + " was not included or is not part of the zombie weapon list." );
3849 
3850  return level.zombie_craftableStubs[ craftable_name ].str_to_craft;
3851 }
3852 
3853 function ‪delete_on_disconnect( craftable, self_notify, skip_delete )
3854 {
3855  craftable endon( "death" );
3856 
3857  self waittill( "disconnect" );
3858 
3859  if ( isdefined( self_notify ) )
3860  {
3861  self notify( self_notify );
3862  }
3863 
3864  if ( !‪IS_TRUE( skip_delete ) )
3865  {
3866  if ( isdefined( craftable.stub ) )
3867  {
3868  thread ‪zm_unitrigger::unregister_unitrigger( craftable.stub );
3869  craftable.stub = undefined;
3870  }
3871 
3872  if ( isdefined( craftable ) )
3873  {
3874  craftable Delete();
3875  }
3876  }
3877 }
3878 
3879 
3880 // Currently unused
3881 //get_craftable_pickup( craftableName, modelName )
3882 //{
3883 // foreach ( craftablePickUp in level.craftablePickUps )
3884 // {
3885 // if ( craftablePickUp[ 0 ].craftableStub.name == craftableName && craftablePickUp[ 0 ].visuals[ 0 ].model == modelName )
3886 // {
3887 // return craftablePickUp[ 0 ];
3888 // }
3889 // }
3890 //
3891 // return undefined;
3892 //}
3893 
3894 
3895 //
3896 // Returns true if the piece is in the shared inventory or if the player is actually holding it
3897 // self is a player
3898 function ‪is_holding_part( craftable_name, piece_name, slot = 0 )
3899 {
3900  // Check against what the player is holding
3901  if ( isdefined( self.current_craftable_pieces ) && isdefined( self.current_craftable_pieces[slot] ) )
3902  {
3903  if ( self.current_craftable_pieces[slot].craftablename == craftable_name &&
3904  self.current_craftable_pieces[slot].modelname == piece_name )
3905  {
3906  return true;
3907  }
3908  }
3909 
3910  // See if the part exists
3911  if ( isdefined( level.a_uts_craftables ) )
3912  {
3913  foreach ( craftable_stub in level.a_uts_craftables )
3914  {
3915  if ( craftable_stub.craftableStub.name == craftable_name )
3916  {
3917  // Search through the pieces to find the part
3918  foreach ( piece in craftable_stub.craftableSpawn.a_pieceSpawns )
3919  {
3920  if ( piece.pieceName == piece_name )
3921  {
3922  if ( ‪IS_TRUE( piece.in_shared_inventory ) )
3923  {
3924  return true;
3925  }
3926  }
3927  }
3928  }
3929  }
3930  }
3931 
3932  return false;
3933 }
3934 
3935 
3936 //
3937 // Returns true if the part has been applied to the craftable, false otherwise
3938 function ‪is_part_crafted( craftable_name, piece_name )
3939 {
3940  // See if the part exists and if it has been crafted
3941  if ( isdefined( level.a_uts_craftables ) )
3942  {
3943  foreach ( craftable_stub in level.a_uts_craftables )
3944  {
3945  if ( craftable_stub.craftableStub.name == craftable_name )
3946  {
3947  // Short circuit
3948  if ( ‪IS_TRUE( craftable_stub.crafted ) )
3949  {
3950  return true;
3951  }
3952 
3953  // Search through the pieces to find the part
3954  foreach ( piece in craftable_stub.craftableSpawn.a_pieceSpawns )
3955  {
3956  if ( piece.pieceName == piece_name )
3957  {
3958  if ( ‪IS_TRUE( piece.crafted ) )
3959  {
3960  return true;
3961  }
3962  }
3963  }
3964  }
3965  }
3966  }
3967 
3968  return false;
3969 }
3970 
3971 
3972 
3973 //-------------------------------------------------
3974 //STAT TRACKING STUFF
3975 //-------------------------------------------------
3976 
3978 {
3979  if ( !isdefined( piece ) || !isdefined( piece.craftablename ) )
3980  {
3981  return;
3982  }
3983 
3984  self ‪add_map_craftable_stat( piece.craftablename, "pieces_pickedup", 1 );
3985  if (isDefined(piece.piecestub) && isDefined(piece.piecestub.hash_id))
3986  {
3987  self RecordMapEvent(‪ZM_MAP_EVENT_CRAFT_PIECE_PICKEDUP, GetTime(), self.origin, level.round_number, piece.piecestub.hash_id);
3988  }
3989  if ( isdefined( piece.piecestub.vox_id ) )
3990  {
3991  if ( ‪IS_TRUE( piece.piecestub.b_one_time_vo ) )
3992  {
3993  if ( !isdefined( self.a_one_time_piece_pickup_vo ) )
3994  {
3995  self.a_one_time_piece_pickup_vo = [];
3996  }
3997 
3998  if ( ‪IS_TRUE( self.dontspeak ) )
3999  {
4000  return;
4001  }
4002 
4003  if ( IsInArray( self.a_one_time_piece_pickup_vo, piece.piecestub.vox_id ) )
4004  {
4005  return;
4006  }
4007 
4008  self.a_one_time_piece_pickup_vo[self.a_one_time_piece_pickup_vo.size] = piece.piecestub.vox_id;
4009  }
4010 
4011  self thread ‪zm_utility::do_player_general_vox( "general", piece.piecestub.vox_id + "_pickup" );
4012  }
4013  else
4014  {
4015  self thread ‪zm_utility::do_player_general_vox( "general", "build_pickup" );
4016  }
4017 }
4018 
4020 {
4021  if ( !isdefined( craftable ) || !isdefined( craftable.craftable_name ) )
4022  {
4023  return;
4024  }
4025 
4026  bname = craftable.craftable_name;
4027 
4028  if ( isdefined( craftable.stat_name ) )
4029  {
4030  bname = craftable.stat_name;
4031  }
4032 
4033  self ‪add_map_craftable_stat( bname, "pieces_built", 1 );
4034 
4035  if ( !craftable ‪craftable_all_crafted() )
4036  {
4037  self thread ‪zm_utility::do_player_general_vox( "general", "build_add" );
4038  }
4039 
4040  //challenge notify
4041  level notify( bname + "_crafted", self );
4042 }
4043 
4044 function ‪track_craftables_crafted( craftable )
4045 {
4046  if ( !isdefined( craftable ) || !isdefined( craftable.craftable_name ) )
4047  {
4048  return;
4049  }
4050 
4051  bname = craftable.craftable_name;
4052 
4053  if ( isdefined( craftable.stat_name ) )
4054  {
4055  bname = craftable.stat_name;
4056  }
4057 
4058  self ‪add_map_craftable_stat( bname, "buildable_built", 1 );
4059  self ‪zm_stats::increment_client_stat( "buildables_built", false );
4060  self ‪zm_stats::increment_player_stat( "buildables_built" );
4061  if (isDefined(craftable.stub) && isDefined(craftable.stub.craftablestub) && isDefined(craftable.stub.craftablestub.hash_id))
4062  {
4063  self RecordMapEvent(‪ZM_MAP_EVENT_CRAFTABLE_BUILT, GetTime(), self.origin, level.round_number, craftable.stub.craftablestub.hash_id);
4064  }
4065 
4066  if( !isdefined(craftable.stub.craftablestub.no_challenge_stat) || (craftable.stub.craftablestub.no_challenge_stat==false) )
4067  {
4068  self ‪zm_stats::increment_challenge_stat( "SURVIVALIST_CRAFTABLE" );
4069  }
4070 
4071  if ( isdefined( craftable.stub.craftablestub.vox_id ) )
4072  {
4073  if ( isdefined( level.zombie_custom_craftable_built_vo ) )
4074  {
4075  self thread [[ level.zombie_custom_craftable_built_vo ]]( craftable.stub );
4076  }
4077 
4078  self thread ‪zm_utility::do_player_general_vox( "general", craftable.stub.craftablestub.vox_id + "_final" );
4079  }
4080 }
4081 
4082 function ‪track_craftables_pickedup( craftable )
4083 {
4084  if ( !isdefined( craftable ) )
4085  {
4086  return;
4087  }
4088 
4089  stat_name = ‪get_craftable_stat_name( craftable.craftable_name );
4090  if (isDefined(craftable.stub) && isDefined(craftable.stub.craftablestub) && isDefined(craftable.stub.craftablestub.hash_id))
4091  {
4092  self RecordMapEvent(‪ZM_MAP_EVENT_CRAFTABLE_PICKEDUP, GetTime(), self.origin, level.round_number, craftable.stub.craftablestub.hash_id);
4093  }
4094 
4095  if ( !isdefined( stat_name ) )
4096  {
4097  return;
4098  }
4099 
4100  self ‪add_map_craftable_stat( stat_name, "buildable_pickedup", 1 );
4101 
4102  if ( isdefined( craftable.stub.craftablestub.vox_id ) )
4103  {
4104  self thread ‪zm_utility::do_player_general_vox( "general", craftable.stub.craftablestub.vox_id + "_plc" );
4105  }
4106 
4107  self ‪say_pickup_craftable_vo( craftable, false );
4108 
4109 }
4110 
4111 //craftables that are planted on the ground ( turret, turbine, etc )
4112 function ‪track_craftables_planted( equipment )
4113 {
4114  if ( !isdefined( equipment ) )
4115  {
4116  return;
4117  }
4118 
4119  craftable_name = undefined;
4120 
4121  if ( isdefined( equipment.name ) )
4122  {
4123  craftable_name = ‪get_craftable_stat_name( equipment.name );
4124  }
4125 
4126  if ( !isdefined( craftable_name ) )
4127  {
4128  return;
4129  }
4130 
4131  ‪demo::bookmark( "zm_player_buildable_placed", gettime(), self );
4132  self ‪add_map_craftable_stat( craftable_name, "buildable_placed", 1 );
4133  if (isDefined(equipment.stub) && isDefined(equipment.stub.craftablestub) && isDefined(equipment.stub.craftablestub.hash_id))
4134  {
4135  self RecordMapEvent(‪ZM_MAP_EVENT_CRAFTABLE_PLANTED, GetTime(), self.origin, level.round_number, equipment.stub.craftablestub.hash_id);
4136  }
4137 //
4138 // vo_name = "craft_plc_" + craftable_name;
4139 // if(craftable_name == "electric_trap")
4140 // {
4141 // vo_name ="craft_plc_trap";
4142 // }
4143 //
4144 // if(!IS_TRUE(self.craftable_timer ))
4145 // {
4146 // self thread zm_utility::do_player_general_vox("general",vo_name);
4147 // self thread placed_craftable_vo_timer();
4148 // }
4149 }
4150 
4152 {
4153  self endon( "disconnect" );
4154  self.craftable_timer = true;
4155  wait( 60 );
4156  self.craftable_timer = false;
4157 }
4158 
4160 {
4161  self endon( "disconnect" );
4162  self.craftable_pickedup_timer = true;
4163  wait( 60 );
4164  self.craftable_pickedup_timer = false;
4165 }
4166 
4167 //craftables that are planted on the ground ( turret, turbine, etc )
4169 {
4170  if ( !isdefined( equipment ) )
4171  {
4172  return;
4173  }
4174 
4175  if ( equipment == "equip_turbine_zm" || equipment == "equip_turret_zm" || equipment == "equip_electrictrap_zm" || equipment == "riotshield_zm" || equipment == "alcatraz_shield_zm" || equipment == "tomb_shield_zm" )
4176  {
4177  self ‪zm_stats::increment_client_stat( "planted_buildables_pickedup", false );
4178  self ‪zm_stats::increment_player_stat( "planted_buildables_pickedup" );
4179  }
4180 
4181  if ( !‪IS_TRUE( self.‪craftable_pickedup_timer ) )
4182  {
4183  self ‪say_pickup_craftable_vo( equipment, true );
4184  self thread ‪craftable_pickedup_timer();
4185  }
4186 }
4187 
4188 
4189 //for things attached (stuff on the bus, etc..)
4190 function ‪track_placed_craftables( craftable_name )
4191 {
4192  if ( !isdefined( craftable_name ) )
4193  {
4194  return;
4195  }
4196 
4197  self ‪add_map_craftable_stat( craftable_name, "buildable_placed", 1 );
4198 
4199  vo_name = undefined;
4200 
4201  if ( craftable_name == level.riotshield_name )
4202  {
4203  vo_name = "craft_plc_shield";
4204  }
4205 
4206  if ( !isdefined( vo_name ) )
4207  {
4208  return;
4209  }
4210 
4211  self thread ‪zm_utility::do_player_general_vox( "general", vo_name );
4212 
4213 }
4214 
4215 
4216 function ‪zombie_craftable_set_record_stats( str_craftable, b_record )
4217 {
4218  ‪DEFAULT(level.craftables_stats_recorded,[]);
4219  level.craftables_stats_recorded[str_craftable] = b_record;
4220 }
4221 
4222 function ‪add_map_craftable_stat( piece_name, stat_name, value )
4223 {
4224  if ( !isdefined( piece_name ) || ( piece_name == "sq_common" ) || ( piece_name == "keys_zm" ) )
4225  {
4226  return;
4227  }
4228 
4229  if ( ‪IS_TRUE( level.zm_disable_recording_stats ) || ‪IS_TRUE( level.zm_disable_recording_buildable_stats ) )
4230  {
4231  return;
4232  }
4233 
4234  ‪DEFAULT(level.craftables_stats_recorded,[]);
4235  if ( !‪IS_TRUE(level.craftables_stats_recorded[piece_name]) )
4236  {
4237  return;
4238  }
4239 
4240  self AddDStat( "buildables", piece_name, stat_name, value );
4241 
4242 }
4243 
4244 function ‪say_pickup_craftable_vo( craftable_name, b_world )
4245 {
4246 // if(IS_TRUE(self.craftable_pickedup_timer))
4247 // {
4248 // return;
4249 // }
4250 // name = get_craftable_vo_name(craftable_name);
4251 //
4252 // if(!isdefined(name))
4253 // {
4254 // return;
4255 // }
4256 //
4257 // vo_name = "craft_pck_b" + name;
4258 // if(IS_TRUE(b_world))
4259 // {
4260 // vo_name = "craft_pck_w" + name;
4261 // }
4262 //
4263 // if(!isdefined(level.transit_craftable_vo_override) || !self [[level.transit_craftable_vo_override]](name,b_world) )
4264 // {
4265 // self thread zm_utility::do_player_general_vox( "general",vo_name );
4266 // self thread craftable_pickedup_timer();
4267 // }
4268 }
4269 
4270 function ‪get_craftable_vo_name( craftable_name )
4271 {
4272 // switch(craftable_name)
4273 // {
4274 // case "equip_turbine_zm": return "turbine";
4275 // case "equip_turret_zm": return "turret";
4276 // case "equip_electrictrap_zm": return "trap";
4277 // case "riotshield_zm": return "shield";
4278 // case "jetgun_zm": return "jetgun";
4279 // case "equip_springpad_zm": return "springpad_zm";
4280 // case "equip_slipgun_zm": return "slipgun_zm";
4281 //
4282 // }
4283 // return undefined;
4284 }
4285 
4286 function ‪get_craftable_stat_name( craftable_name )
4287 {
4288  if ( isdefined( craftable_name ) )
4289  {
4290  switch ( craftable_name )
4291  {
4292  case "equip_riotshield_zm":
4293  return "riotshield_zm";
4294 
4295  case "equip_turbine_zm":
4296  return "turbine";
4297 
4298  case "equip_turret_zm":
4299  return "turret";
4300 
4301  case "equip_electrictrap_zm":
4302  return "electric_trap";
4303 
4304  case "equip_springpad_zm":
4305  return "springpad_zm";
4306 
4307  case "equip_slipgun_zm":
4308  return "slipgun_zm";
4309  }
4310  }
4311 
4312  return craftable_name;
4313 }
4314 
4315 
4316 //
4317 // Return the model entity on the craftable table, if one exists.
4318 function ‪get_craftable_model( str_craftable )
4319 {
4320  foreach ( uts_craftable in level.a_uts_craftables )
4321  {
4322  if ( uts_craftable.craftableStub.name == str_craftable )
4323  {
4324  if ( isdefined( uts_craftable.model ) )
4325  {
4326  return uts_craftable.model;
4327  }
4328 
4329  break;
4330  }
4331  }
4332 
4333  return undefined;
4334 }
4335 
4336 
4337 //
4338 // Returns the craftable pieceSpawn, if one exists.
4339 function ‪get_craftable_piece( str_craftable, str_piece )
4340 {
4341  foreach ( uts_craftable in level.a_uts_craftables )
4342  {
4343  if ( uts_craftable.craftableStub.name == str_craftable )
4344  {
4345  foreach ( pieceSpawn in uts_craftable.craftableSpawn.a_pieceSpawns )
4346  {
4347  if ( pieceSpawn.pieceName == str_piece )
4348  {
4349  return pieceSpawn;
4350  }
4351  }
4352 
4353  break;
4354  }
4355  }
4356 
4357  return undefined;
4358 }
4359 
4360 
4361 //
4362 // Give the player the piece specified, as if he picked it up
4363 // self is a player
4364 function ‪player_get_craftable_piece( str_craftable, str_piece )
4365 {
4366  pieceSpawn = ‪get_craftable_piece( str_craftable, str_piece );
4367 
4368  if ( isdefined( pieceSpawn ) )
4369  {
4370  self ‪player_take_piece( pieceSpawn );
4371  }
4372 }
4373 
4374 //
4375 // Take the specified craftable piece away from the player
4376 // self is a player
4377 function ‪player_remove_craftable_piece( str_craftable, str_piece )
4378 {
4379  pieceSpawn = ‪get_craftable_piece( str_craftable, str_piece );
4380 
4381  if ( isdefined( pieceSpawn ) )
4382  {
4383  self ‪player_remove_piece( pieceSpawn );
4384  }
4385 }
4386 
4387 // remove piece from player and reset the player's craftable clientfield - only does this, and does NOT spawn the piece back into the world
4388 function ‪player_remove_piece( piece_to_remove )
4389 {
4390  ‪DEFAULT( self.current_craftable_pieces, [] );
4391 
4392  foreach ( slot, self_piece in self.current_craftable_pieces )
4393  {
4394  if( piece_to_remove.pieceName === self_piece.pieceName && piece_to_remove.craftableName === self_piece.craftableName )
4395  {
4397  self.current_craftable_pieces[ slot ] = undefined;
4398  self notify( "craftable_piece_released" + slot );
4399  }
4400  }
4401 }
4402 
4403 //
4404 // Returns the model entity for a craftable, if one exists.
4405 function ‪get_craftable_piece_model( str_craftable, str_piece )
4406 {
4407  foreach ( uts_craftable in level.a_uts_craftables )
4408  {
4409  if ( uts_craftable.craftableStub.name == str_craftable )
4410  {
4411  foreach ( pieceSpawn in uts_craftable.craftableSpawn.a_pieceSpawns )
4412  {
4413  if ( pieceSpawn.pieceName == str_piece && isdefined( pieceSpawn.model ) )
4414  {
4415  return pieceSpawn.model;
4416  }
4417  }
4418 
4419  break;
4420  }
4421  }
4422 
4423  return undefined;
4424 }
4425 
4426 // play crafted-item ui
4427 // if b_is_crafted is false and str_widget_clientuimodel is undefined, show the general craftable that has the highest number of pieces (shield wins ties)
4428 function ‪player_show_craftable_parts_ui( str_crafted_clientuimodel, str_widget_clientuimodel, b_is_crafted )
4429 {
4430  self notify( "player_show_craftable_parts_ui" );
4431  self endon( "player_show_craftable_parts_ui" );
4432 
4433  if( b_is_crafted )
4434  {
4435  if( isdefined( str_crafted_clientuimodel ) )
4436  {
4437  self thread ‪clientfield::set_player_uimodel( str_crafted_clientuimodel, 1 );
4438  }
4439  n_show_ui_duration = ‪ZM_CRAFTABLES_FULLY_CRAFTED_UI_DURATION;
4440  }
4441  else
4442  {
4444  }
4445 
4446  self thread ‪player_hide_craftable_parts_ui_after_duration( str_widget_clientuimodel, n_show_ui_duration );
4447 }
4448 
4449 function ‪player_hide_craftable_parts_ui_after_duration( str_widget_clientuimodel, n_show_ui_duration )
4450 {
4451  self endon( "disconnect" );
4452 
4453  self thread ‪clientfield::set_player_uimodel( str_widget_clientuimodel, 1 );
4454  wait n_show_ui_duration;
4455  self thread ‪clientfield::set_player_uimodel( str_widget_clientuimodel, 0 );
4456 }
4457 
‪piece_spawn_in
‪function piece_spawn_in(pieceStub)
Definition: _zm_craftables.gsc:1315
‪craftable_is_piece_crafted
‪function craftable_is_piece_crafted(piece)
Definition: _zm_craftables.gsc:2090
‪player_explode_craftable
‪function player_explode_craftable(equipname, origin, speed, return_to_spawn, return_time)
Definition: _zm_craftables.gsc:3521
‪update_open_table_status
‪function update_open_table_status()
Definition: _zm_craftables.gsc:2437
‪waittill_crafted
‪function waittill_crafted(craftable_name)
Definition: _zm_craftables.gsc:2188
‪is_equipment
‪function is_equipment(weapon)
Definition: _zm_equipment.gsc:691
‪player_can_craft
‪function player_can_craft(craftableSpawn, continuing, slot)
Definition: _zm_craftables.gsc:2195
‪createPrimaryProgressBarText
‪function createPrimaryProgressBarText()
Definition: hud_util_shared.gsc:612
‪piece_unitrigger_think
‪function piece_unitrigger_think()
Definition: _zm_craftables.gsc:806
‪generate_zombie_craftable_piece
‪function generate_zombie_craftable_piece(craftablename, pieceName, radius, height, drop_offset, hud_icon, onPickup, onDrop, onCrafted, use_spawn_num, tag_name, can_reuse, client_field_value, is_shared=FALSE, vox_id, b_one_time_vo=false, hint_string, slot=0)
Definition: _zm_craftables.gsc:416
‪player_get_craftable_piece
‪function player_get_craftable_piece(str_craftable, str_piece)
Definition: _zm_craftables.gsc:4364
‪is_part_crafted
‪function is_part_crafted(craftable_name, piece_name)
Definition: _zm_craftables.gsc:3938
‪ExplosionDamage
‪function ExplosionDamage(damage, pos)
Definition: _zm_craftables.gsc:258
‪piece_unspawn
‪function piece_unspawn()
Definition: _zm_craftables.gsc:1466
‪PIECE_UNITRIGGER_OFFSET
‪#define PIECE_UNITRIGGER_OFFSET
Definition: _zm_craftables.gsc:602
‪get_actual_uts_craftable
‪function get_actual_uts_craftable()
Definition: _zm_craftables.gsc:1920
‪get_craftable_stat_name
‪function get_craftable_stat_name(craftable_name)
Definition: _zm_craftables.gsc:4286
‪increment_client_stat
‪function increment_client_stat(stat_name, include_gametype)
Definition: _zm_stats.gsc:389
‪player_remove_piece
‪function player_remove_piece(piece_to_remove)
Definition: _zm_craftables.gsc:4388
‪set_piece_count
‪function set_piece_count(n_count)
Definition: _zm_craftables.gsc:362
‪on_finalize_initialization
‪function on_finalize_initialization(func, obj)
Definition: callbacks_shared.csc:203
‪craftable_play_craft_fx
‪function craftable_play_craft_fx(player)
Definition: _zm_craftables.gsc:3055
‪anystub_get_unitrigger_origin
‪function anystub_get_unitrigger_origin()
Definition: _zm_craftables.gsc:194
‪think_craftables
‪function think_craftables()
Definition: _zm_craftables.gsc:3568
‪onPickupUTS
‪function onPickupUTS(player)
Definition: _zm_craftables.gsc:3735
‪add_zombie_craftable
‪function add_zombie_craftable(craftable_name, str_to_craft, str_crafting, str_taken, onFullyCrafted, need_all_pieces)
Definition: _zm_craftables.gsc:294
‪increment_player_stat
‪function increment_player_stat(stat_name)
Definition: _zm_stats.gsc:369
‪hide_craftable_table_model
‪function hide_craftable_table_model(trigger_targetname)
Definition: _zm_craftables.gsc:1633
‪craftable_set_piece_crafted
‪function craftable_set_piece_crafted(pieceSpawn_check, player)
Definition: _zm_craftables.gsc:2003
‪set_to_player
‪function set_to_player(str_field_name, n_value)
Definition: clientfield_shared.gsc:58
‪limited_weapon_below_quota
‪function limited_weapon_below_quota(weapon, ignore_player, pap_triggers)
Definition: _zm_weapons.gsc:696
‪openTableCraftable
‪function openTableCraftable()
Definition: _zm_craftables.gsc:3581
‪find_craftable_stub
‪function find_craftable_stub(equipname)
Definition: _zm_craftables.gsc:3458
‪get_craftable_piece_model
‪function get_craftable_piece_model(str_craftable, str_piece)
Definition: _zm_craftables.gsc:4405
‪onSpawn
‪function onSpawn(watcher, player)
Definition: _ballistic_knife.gsc:21
‪piecetrigger_update_prompt
‪function piecetrigger_update_prompt(player)
Definition: _zm_craftables.gsc:741
‪UNCRAFT
‪#define UNCRAFT
Definition: _zm_craftables.gsh:11
‪VERSION_SHIP
‪#define VERSION_SHIP
Definition: version.gsh:36
‪onBeginUseUTS
‪function onBeginUseUTS(player)
Definition: _zm_craftables.gsc:3744
‪do_player_general_vox
‪function do_player_general_vox(category, type, timer, chance)
Definition: _zm_utility.gsc:5317
‪waittill_any_return
‪function waittill_any_return(string1, string2, string3, string4, string5, string6, string7)
Definition: util_shared.csc:212
‪ZM_MAP_EVENT_CRAFTABLE_PLANTED
‪#define ZM_MAP_EVENT_CRAFTABLE_PLANTED
Definition: _zm_utility.gsh:58
‪can_craft_shared_piece
‪function can_craft_shared_piece(continuing)
Definition: _zm_craftables.gsc:2116
‪get_array
‪function get_array(kvp_value, kvp_key="targetname")
Definition: struct.csc:34
‪ONE_USE_AND_FLY
‪#define ONE_USE_AND_FLY
Definition: _zm_buildables.gsh:16
‪disable_player_move_states
‪function disable_player_move_states(forceStanceChange)
Definition: _zm_utility.gsc:5536
‪piece_pick_random_spawn
‪function piece_pick_random_spawn()
Definition: _zm_craftables.gsc:1278
‪player_drop_piece_on_downed
‪function player_drop_piece_on_downed(slot)
Definition: _zm_craftables.gsc:548
‪unitrigger_trigger
‪function unitrigger_trigger(player)
Definition: _zm_unitrigger.gsc:65
‪player_show_craftable_parts_ui
‪function player_show_craftable_parts_ui(str_crafted_clientuimodel, str_widget_clientuimodel, b_is_crafted)
Definition: _zm_craftables.gsc:4428
‪ZM_CRAFTABLES_FULLY_CRAFTED_UI_DURATION
‪#define ZM_CRAFTABLES_FULLY_CRAFTED_UI_DURATION
Definition: _zm_craftables.gsc:97
‪craftable_piece_unitriggers
‪function craftable_piece_unitriggers(craftable_name, origin)
Definition: _zm_craftables.gsc:1569
‪PERSISTENT
‪#define PERSISTENT
Definition: _zm_buildables.gsh:17
‪player_hide_craftable_parts_ui_after_duration
‪function player_hide_craftable_parts_ui_after_duration(str_widget_clientuimodel, n_show_ui_duration)
Definition: _zm_craftables.gsc:4449
‪destroyElem
‪function destroyElem()
Definition: hud_util_shared.gsc:755
‪craftable_trigger_think_array
‪function craftable_trigger_think_array(trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:3601
‪CRAFTABLE_OBTAINED
‪#define CRAFTABLE_OBTAINED
Definition: _zm_craftables.gsh:16
‪track_craftable_piece_pickedup
‪function track_craftable_piece_pickedup(piece)
Definition: _zm_craftables.gsc:3977
‪craftable_has_piece
‪function craftable_has_piece(piece)
Definition: _zm_craftables.gsc:1903
‪spawn
‪function spawn(v_origin=(0, 0, 0), v_angles=(0, 0, 0))
Definition: struct.csc:23
‪if
‪if(on_off)
Definition: util_shared.csc:908
‪piece_hide
‪function piece_hide()
Definition: _zm_craftables.gsc:1488
‪IS_TRUE
‪#define IS_TRUE(__a)
Definition: shared.gsh:251
‪get
‪function get(kvp_value, kvp_key="targetname")
Definition: struct.csc:13
‪is_holding_part
‪function is_holding_part(craftable_name, piece_name, slot=0)
Definition: _zm_craftables.gsc:3898
‪player_progress_bar
‪function player_progress_bar(start_time, craft_time)
Definition: _zm_craftables.gsc:2921
‪craftable_use_hold_think_internal
‪function craftable_use_hold_think_internal(player, slot=self.stub.craftableSpawn.inventory_slot)
Definition: _zm_craftables.gsc:2939
‪open_craftablestub_update_prompt
‪function open_craftablestub_update_prompt(player, slot=0)
Definition: _zm_craftables.gsc:2731
‪player_remove_craftable_piece
‪function player_remove_craftable_piece(str_craftable, str_piece)
Definition: _zm_craftables.gsc:4377
‪piece_show
‪function piece_show()
Definition: _zm_craftables.gsc:1496
‪setup_vehicle_unitrigger_craftable
‪function setup_vehicle_unitrigger_craftable(parent, trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:3611
‪enable_player_move_states
‪function enable_player_move_states()
Definition: _zm_utility.gsc:5554
‪get_actual_craftableSpawn
‪function get_actual_craftableSpawn()
Definition: _zm_craftables.gsc:1940
‪is_player_valid
‪function is_player_valid(player, checkIgnoreMeFlag, ignore_laststand_players)
Definition: skeleton.gsc:256
‪setup_unitrigger_craftable_internal
‪function setup_unitrigger_craftable_internal(trig, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:1683
‪init
‪function init()
Definition: _zm_craftables.gsc:108
‪buy
‪function buy(equipment)
Definition: _zm_equipment.gsc:374
‪say_pickup_craftable_vo
‪function say_pickup_craftable_vo(craftable_name, b_world)
Definition: _zm_craftables.gsc:4244
‪piece_deallocate_spawn
‪function piece_deallocate_spawn()
Definition: _zm_craftables.gsc:1265
‪craftable_place_think
‪function craftable_place_think()
Definition: _zm_craftables.gsc:3086
‪unregister_unitrigger
‪function unregister_unitrigger(unitrigger_stub)
Definition: _zm_unitrigger.gsc:173
‪damage
‪function damage(trap)
Definition: _zm_trap_electric.gsc:116
‪increment_is_drinking
‪function increment_is_drinking()
Definition: _zm_utility.gsc:3859
‪switch_back_primary_weapon
‪function switch_back_primary_weapon(oldprimary, immediate=false)
Definition: _zm_weapons.gsc:280
‪get_nonalternate_weapon
‪function get_nonalternate_weapon(weapon)
Definition: aat_shared.gsc:104
‪craftable_transfer_data
‪function craftable_transfer_data()
Definition: _zm_craftables.gsc:2273
‪craftable_use_hold_think
‪function craftable_use_hold_think(player)
Definition: _zm_craftables.gsc:3069
‪get_closest_unitriggers
‪function get_closest_unitriggers(org, array, dist=9999999)
Definition: _zm_unitrigger.gsc:818
‪increment_challenge_stat
‪function increment_challenge_stat(stat_name, amount=1)
Definition: _zm_stats.gsc:478
‪model_fly_away
‪function model_fly_away(unitrigger)
Definition: _zm_craftables.gsc:3434
‪player_finish_craftable
‪function player_finish_craftable(craftableSpawn)
Definition: _zm_craftables.gsc:2480
‪createPrimaryProgressBar
‪function createPrimaryProgressBar()
Definition: hud_util_shared.gsc:602
‪DEFAULT
‪#define DEFAULT(__var, __default)
Definition: shared.gsh:270
‪add_zombie_craftable_vox_category
‪function add_zombie_craftable_vox_category(craftable_name, vox_id)
Definition: _zm_craftables.gsc:368
‪watch_hit_players
‪function watch_hit_players()
Definition: _zm_craftables.gsc:943
‪stub_uncraft_craftable
‪function stub_uncraft_craftable(stub, return_pieces, origin, angles, use_random_start)
Definition: _zm_craftables.gsc:3478
‪player_throw_piece
‪function player_throw_piece(piece, origin, dir, return_to_spawn, return_time, endangles)
Definition: _zm_craftables.gsc:870
‪add_craftable_piece
‪function add_craftable_piece(pieceStub, tag_name, can_reuse)
Definition: _zm_craftables.gsc:522
‪updateBar
‪function updateBar(barFrac, rateOfChange)
Definition: hud_util_shared.gsc:294
‪ONE_TIME_CRAFT
‪#define ONE_TIME_CRAFT
Definition: _zm_craftables.gsh:14
‪craftable_crafted
‪function craftable_crafted()
Definition: _zm_craftables.gsc:3830
‪ZM_CRAFTABLES_LOOKAT_DOT
‪#define ZM_CRAFTABLES_LOOKAT_DOT
Definition: _zm_craftables.gsc:95
‪__main__
‪function __main__()
Definition: _zm_craftables.gsc:147
‪craftable_all_crafted
‪function craftable_all_crafted()
Definition: _zm_craftables.gsc:2163
‪REGISTER_SYSTEM_EX
‪#define REGISTER_SYSTEM_EX(__sys, __func_init_preload, __func_init_postload, __reqs)
Definition: shared.gsh:209
‪track_craftable_pieces_crafted
‪function track_craftable_pieces_crafted(craftable)
Definition: _zm_craftables.gsc:4019
‪player_destroy_piece
‪function player_destroy_piece(piece, slot)
Definition: _zm_craftables.gsc:1127
‪onPlayerLastStand
‪function onPlayerLastStand()
Definition: _zm_craftables.gsc:561
‪track_craftables_pickedup
‪function track_craftables_pickedup(craftable)
Definition: _zm_craftables.gsc:4082
‪craftable_pickedup_timer
‪function craftable_pickedup_timer()
Definition: _zm_craftables.gsc:4159
‪combine_craftable_pieces
‪function combine_craftable_pieces(piece1, piece2, piece3)
Definition: _zm_craftables.gsc:496
‪craftables_watch_swipes
‪function craftables_watch_swipes()
Definition: _zm_craftables.gsc:214
‪craftablestub_remove
‪function craftablestub_remove()
Definition: _zm_craftables.gsc:2512
‪setup_unitrigger_craftable_array
‪function setup_unitrigger_craftable_array(trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:1668
‪craftabletrigger_update_prompt
‪function craftabletrigger_update_prompt(player)
Definition: _zm_craftables.gsc:2517
‪is_equipment_that_blocks_purchase
‪function is_equipment_that_blocks_purchase(weapon)
Definition: _zm_equipment.gsc:701
‪claim_location
‪function claim_location(location)
Definition: _zm_craftables.gsc:1144
‪player_continue_crafting
‪function player_continue_crafting(craftableSpawn, slot)
Definition: _zm_craftables.gsc:2836
‪anystub_update_prompt
‪function anystub_update_prompt(player)
Definition: _zm_craftables.gsc:157
‪decrement_is_drinking
‪function decrement_is_drinking()
Definition: _zm_utility.gsc:3898
‪complete_craftable
‪function complete_craftable(str_craftable_name)
Definition: _zm_craftables.gsc:2491
‪set_craftable_clientfield
‪function set_craftable_clientfield()
Definition: _zm_craftables.gsc:152
‪is_enabled
‪function is_enabled(name)
Definition: _zm_bgb.gsc:4
‪unitrigger_origin
‪function unitrigger_origin()
Definition: _zm_unitrigger.gsc:77
‪manage_multiple_pieces
‪function manage_multiple_pieces(max_instances)
Definition: _zm_craftables.gsc:488
‪wait_till
‪function wait_till(str_flag)
Definition: flag_shared.csc:189
‪is_player_looking_at
‪function is_player_looking_at(origin, dot, do_trace, ignore_ent)
Definition: util_shared.gsc:1664
‪piece_spawn_at
‪function piece_spawn_at(origin, angles, use_random_start)
Definition: _zm_craftables.gsc:1379
‪finish_crafting_shared_piece
‪function finish_crafting_shared_piece()
Definition: _zm_craftables.gsc:2110
‪track_craftables_planted
‪function track_craftables_planted(equipment)
Definition: _zm_craftables.gsc:4112
‪onDropUTS
‪function onDropUTS(player)
Definition: _zm_craftables.gsc:3739
‪get_hide_model_if_unavailable
‪function get_hide_model_if_unavailable(craftable_name)
Definition: _zm_craftables.gsc:345
‪bookmark
‪function bookmark(type, time, mainClientEnt, otherClientEnt, eventPriority, inflictorEnt, overrideEntityCamera, actorEnt)
Definition: demo_shared.gsc:25
‪player_can_take_piece
‪function player_can_take_piece(piece)
Definition: _zm_craftables.gsc:859
‪craftable_is_piece_crafted_or_crafting
‪function craftable_is_piece_crafted_or_crafting(piece)
Definition: _zm_craftables.gsc:2152
‪player_drop_piece_on_death
‪function player_drop_piece_on_death(slot=0)
Definition: _zm_craftables.gsc:1013
‪player_progress_bar_update
‪function player_progress_bar_update(start_time, craft_time)
Definition: _zm_craftables.gsc:2894
‪DELETE_TRIGGER
‪#define DELETE_TRIGGER
Definition: _zm_buildables.gsh:9
‪get_craftable_model
‪function get_craftable_model(str_craftable)
Definition: _zm_craftables.gsc:4318
‪CLIENTFIELD_CRAFTABLE
‪#define CLIENTFIELD_CRAFTABLE
Definition: _zm_craftables.gsh:4
‪Spawn
‪function Spawn(parent, onDeathCallback)
Definition: _flak_drone.gsc:427
‪has_player_equipment
‪function has_player_equipment(weapon)
Definition: _zm_equipment.gsc:731
‪is_limited
‪function is_limited(equipment)
Definition: _zm_equipment.gsc:438
‪get_craftable_vo_name
‪function get_craftable_vo_name(craftable_name)
Definition: _zm_craftables.gsc:4270
‪piece_set_spawn
‪function piece_set_spawn(num)
Definition: _zm_craftables.gsc:1302
‪piecestub_get_unitrigger_origin
‪function piecestub_get_unitrigger_origin()
Definition: _zm_craftables.gsc:604
‪get_craftable_hint
‪function get_craftable_hint(craftable_name)
Definition: _zm_craftables.gsc:3846
‪ignore_triggers
‪function ignore_triggers(timer)
Definition: _zm_utility.csc:21
‪onDrop
‪function onDrop(player)
Definition: ctf.gsc:707
‪player_return_piece_to_original_spawn
‪function player_return_piece_to_original_spawn(slot=0)
Definition: _zm_craftables.gsc:993
‪onUsePlantObjectUTS
‪function onUsePlantObjectUTS(player)
Definition: _zm_craftables.gsc:3787
‪get_craftable_piece
‪function get_craftable_piece(str_craftable, str_piece)
Definition: _zm_craftables.gsc:4339
‪set
‪function set(str_field_name, n_value)
Definition: clientfield_shared.gsc:34
‪IS_EQUAL
‪#define IS_EQUAL(__a, __b)
Definition: shared.gsh:250
‪set_build_time
‪function set_build_time(craftable_name, build_time)
Definition: _zm_craftables.gsc:354
‪placed_craftable_vo_timer
‪function placed_craftable_vo_timer()
Definition: _zm_craftables.gsc:4151
‪delete_on_disconnect
‪function delete_on_disconnect(craftable, self_notify, skip_delete)
Definition: _zm_craftables.gsc:3853
‪__init__
‪function __init__()
Definition: _zm_craftables.gsc:103
‪ZM_MAP_EVENT_CRAFTABLE_PICKEDUP
‪#define ZM_MAP_EVENT_CRAFTABLE_PICKEDUP
Definition: _zm_utility.gsh:59
‪track_craftables_crafted
‪function track_craftables_crafted(craftable)
Definition: _zm_craftables.gsc:4044
‪setup_craftable_pieces
‪function setup_craftable_pieces()
Definition: _zm_craftables.gsc:1884
‪piece_wait_and_return
‪function piece_wait_and_return(return_time)
Definition: _zm_craftables.gsc:961
‪choose_open_craftable
‪function choose_open_craftable(player)
Definition: _zm_craftables.gsc:2633
‪player_take_piece
‪function player_take_piece(pieceSpawn)
Definition: _zm_craftables.gsc:1066
‪player_is_in_laststand
‪function player_is_in_laststand()
Definition: laststand_shared.gsc:18
‪track_planted_craftables_pickedup
‪function track_planted_craftables_pickedup(equipment)
Definition: _zm_craftables.gsc:4168
‪onEndUseUTS
‪function onEndUseUTS(team, player, result)
Definition: _zm_craftables.gsc:3758
‪generate_piece_unitrigger
‪function generate_piece_unitrigger(classname, origin, angles, flags, radius, script_height, hint_string, moving, b_nolook)
Definition: _zm_craftables.gsc:614
‪uncraft_craftable
‪function uncraft_craftable(equipname, return_pieces, origin, angles)
Definition: _zm_craftables.gsc:3471
‪ZM_MAP_EVENT_CRAFT_PIECE_PICKEDUP
‪#define ZM_MAP_EVENT_CRAFT_PIECE_PICKEDUP
Definition: _zm_utility.gsh:56
‪set_player_uimodel
‪function set_player_uimodel(str_field_name, n_value)
Definition: clientfield_shared.gsc:75
‪piecestub_update_prompt
‪function piecestub_update_prompt(player, slot=self.piece.inventory_slot)
Definition: _zm_craftables.gsc:755
‪is_point_in_craft_trigger
‪function is_point_in_craft_trigger(point)
Definition: _zm_craftables.gsc:1162
‪weapon_give
‪function weapon_give(weapon, is_upgrade=false, magic_box=false, nosound=false, b_switch_weapon=true)
Definition: _zm_weapons.gsc:2603
‪vehicle_craftable_trigger_think
‪function vehicle_craftable_trigger_think(vehicle, trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:3728
‪destroy
‪function destroy(watcher, owner)
Definition: _decoy.gsc:108
‪craftable_is_piece_crafting
‪function craftable_is_piece_crafting(pieceSpawn_check)
Definition: _zm_craftables.gsc:2125
‪generate_piece
‪function generate_piece(pieceStub)
Definition: _zm_craftables.gsc:1507
‪start_crafting_shared_piece
‪function start_crafting_shared_piece()
Definition: _zm_craftables.gsc:2104
‪onCantUseUTS
‪function onCantUseUTS(player)
Definition: _zm_craftables.gsc:3779
‪ZM_CRAFTABLES_NOT_ENOUGH_PIECES_UI_DURATION
‪#define ZM_CRAFTABLES_NOT_ENOUGH_PIECES_UI_DURATION
Definition: _zm_craftables.gsc:96
‪set_hide_model_if_unavailable
‪function set_hide_model_if_unavailable(craftable_name, hide_when_unavailable)
Definition: _zm_craftables.gsc:337
‪zombie_craftable_set_record_stats
‪function zombie_craftable_set_record_stats(str_craftable, b_record)
Definition: _zm_craftables.gsc:4216
‪craftable_trigger_think
‪function craftable_trigger_think(trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:3596
‪unitrigger_force_per_player_triggers
‪function unitrigger_force_per_player_triggers(unitrigger_stub, opt_on_off)
Definition: _zm_unitrigger.gsc:58
‪make_zombie_craftable_open
‪function make_zombie_craftable_open(str_craftable, str_model, v_angle_offset, v_origin_offset)
Definition: _zm_craftables.gsc:271
‪in_revive_trigger
‪function in_revive_trigger()
Definition: _zm_utility.gsc:1684
‪craftable_complete
‪function craftable_complete()
Definition: _zm_craftables.gsc:3835
‪on_connect
‪function on_connect()
Definition: _arena.gsc:20
‪craftable_set_piece_crafting
‪function craftable_set_piece_crafting(pieceSpawn_check)
Definition: _zm_craftables.gsc:2045
‪result
‪function result(death, attacker, mod, weapon)
Definition: _zm_aat_blast_furnace.gsc:46
‪craftable_clear_piece_crafting
‪function craftable_clear_piece_crafting(pieceSpawn_check)
Definition: _zm_craftables.gsc:2071
‪track_placed_craftables
‪function track_placed_craftables(craftable_name)
Definition: _zm_craftables.gsc:4190
‪IS_DRINKING
‪#define IS_DRINKING(_is_drinking)
Definition: _zm_utility.gsh:1
‪craftable_can_use_shared_piece
‪function craftable_can_use_shared_piece()
Definition: _zm_craftables.gsc:1959
‪register_static_unitrigger
‪function register_static_unitrigger(unitrigger_stub, trigger_func, recalculate_zone)
Definition: _zm_unitrigger.gsc:236
‪CRAFTABLE_INVALID_CHOICE
‪#define CRAFTABLE_INVALID_CHOICE
Definition: _zm_craftables.gsh:20
‪name
‪class GroundFx name
‪is_placeable_mine
‪function is_placeable_mine(weapon)
Definition: _zm_utility.gsc:4267
‪add_map_craftable_stat
‪function add_map_craftable_stat(piece_name, stat_name, value)
Definition: _zm_craftables.gsc:4222
‪craftablestub_update_prompt
‪function craftablestub_update_prompt(player, unitrigger, slot=self.craftableStub.inventory_slot)
Definition: _zm_craftables.gsc:2530
‪player_craft
‪function player_craft(craftableSpawn, slot=craftableSpawn.inventory_slot)
Definition: _zm_craftables.gsc:2315
‪CLIENTFIELD_CRAFTABLE_PIECE_NONE
‪#define CLIENTFIELD_CRAFTABLE_PIECE_NONE
Definition: _zm_craftables.gsh:1
‪tag_name
‪var tag_name
Definition: _driving_fx.csc:74
‪anystub_on_spawn_trigger
‪function anystub_on_spawn_trigger(trigger)
Definition: _zm_craftables.gsc:204
‪ZM_MAP_EVENT_CRAFTABLE_BUILT
‪#define ZM_MAP_EVENT_CRAFTABLE_BUILT
Definition: _zm_utility.gsh:57
‪piece_allocate_spawn
‪function piece_allocate_spawn(pieceStub)
Definition: _zm_craftables.gsc:1201
‪disappear_fx
‪function disappear_fx(origin, fx, angles)
Definition: _zm_equipment.gsc:655
‪start_ammo
‪function start_ammo(equipment)
Definition: _zm_equipment.gsc:617
‪is_craftable
‪function is_craftable()
Definition: _zm_craftables.gsc:3799
‪onPickup
‪function onPickup(event)
Definition: _dogtags.gsc:225
‪limited_in_use
‪function limited_in_use(equipment)
Definition: _zm_equipment.gsc:454
‪register_unitrigger
‪function register_unitrigger(unitrigger_stub, trigger_func)
Definition: _zm_unitrigger.gsc:166
‪include_zombie_craftable
‪function include_zombie_craftable(craftableStub)
Definition: _zm_craftables.gsc:375
‪player_drop_piece
‪function player_drop_piece(piece, slot)
Definition: _zm_craftables.gsc:1040
‪setup_unitrigger_craftable
‪function setup_unitrigger_craftable(trigger_targetname, equipname, weaponname, trigger_hintstring, delete_trigger, persistent)
Definition: _zm_craftables.gsc:1655
‪WAIT_SERVER_FRAME
‪#define WAIT_SERVER_FRAME
Definition: shared.gsh:265