1 //********************************************************************************************
2 //*
3 //* This file is part of Egoboo.
4 //*
5 //* Egoboo is free software: you can redistribute it and/or modify it
6 //* under the terms of the GNU General Public License as published by
7 //* the Free Software Foundation, either version 3 of the License, or
8 //* (at your option) any later version.
9 //*
10 //* Egoboo is distributed in the hope that it will be useful, but
11 //* WITHOUT ANY WARRANTY; without even the implied warranty of
12 //* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 //* General Public License for more details.
14 //*
15 //* You should have received a copy of the GNU General Public License
16 //* along with Egoboo. If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19
20 /// @file enchant.c
21 /// @brief handles enchantments attached to objects
22 /// @details
23
24 #include "enchant.inl"
25
26 #include "char.inl"
27 #include "mad.h"
28 #include "particle.inl"
29 #include "profile.inl"
30
31 #include "sound.h"
32 #include "camera.h"
33 #include "game.h"
34 #include "script_functions.h"
35 #include "log.h"
36
37 #include "egoboo_fileutil.h"
38 #include "egoboo.h"
39
40 //--------------------------------------------------------------------------------------------
41 //--------------------------------------------------------------------------------------------
42 INSTANTIATE_STACK( ACCESS_TYPE_NONE, eve_t, EveStack, MAX_EVE );
43
44 //--------------------------------------------------------------------------------------------
45 //--------------------------------------------------------------------------------------------
46 static bool_t enc_free( enc_t * penc );
47
48 static enc_t * enc_config_ctor( enc_t * penc );
49 static enc_t * enc_config_init( enc_t * penc );
50 static enc_t * enc_config_deinit( enc_t * penc );
51 static enc_t * enc_config_active( enc_t * penc );
52 static enc_t * enc_config_dtor( enc_t * penc );
53
54 //--------------------------------------------------------------------------------------------
55 //--------------------------------------------------------------------------------------------
enchant_system_begin()56 void enchant_system_begin()
57 {
58 EncList_init();
59 init_all_eve();
60 }
61
62 //--------------------------------------------------------------------------------------------
enchant_system_end()63 void enchant_system_end()
64 {
65 release_all_eve();
66 EncList_dtor();
67 }
68
69 //--------------------------------------------------------------------------------------------
70 //--------------------------------------------------------------------------------------------
enc_free(enc_t * penc)71 bool_t enc_free( enc_t * penc )
72 {
73 if ( !ALLOCATED_PENC( penc ) ) return bfalse;
74
75 // nothing to do yet
76
77 return btrue;
78 }
79
80 //--------------------------------------------------------------------------------------------
enc_ctor(enc_t * penc)81 enc_t * enc_ctor( enc_t * penc )
82 {
83 obj_data_t save_base;
84 obj_data_t * pbase;
85
86 // grab the base object
87 if ( NULL == penc ) return NULL;
88 pbase = POBJ_GET_PBASE( penc );
89
90 memcpy( &save_base, pbase, sizeof( obj_data_t ) );
91
92 memset( penc, 0, sizeof( *penc ) );
93
94 // restore the base object data
95 memcpy( pbase, &save_base, sizeof( obj_data_t ) );
96
97 // reset the base counters
98 pbase->update_count = 0;
99 pbase->frame_count = 0;
100
101 penc->profile_ref = ( PRO_REF )MAX_PROFILE;
102 penc->eve_ref = ( EVE_REF )MAX_EVE;
103
104 penc->target_ref = ( CHR_REF )MAX_CHR;
105 penc->owner_ref = ( CHR_REF )MAX_CHR;
106 penc->spawner_ref = ( CHR_REF )MAX_CHR;
107 penc->spawnermodel_ref = ( PRO_REF )MAX_PROFILE;
108 penc->overlay_ref = ( CHR_REF )MAX_CHR;
109
110 penc->nextenchant_ref = ( ENC_REF )MAX_ENC;
111
112 // we are done constructing. move on to initializing.
113 pbase->state = ego_object_initializing;
114
115 return penc;
116 }
117
118 //--------------------------------------------------------------------------------------------
enc_dtor(enc_t * penc)119 enc_t * enc_dtor( enc_t * penc )
120 {
121 if ( NULL == penc ) return penc;
122
123 // destroy the object
124 enc_free( penc );
125
126 // Destroy the base object.
127 // Sets the state to ego_object_terminated automatically.
128 POBJ_TERMINATE( penc );
129
130 return penc;
131 }
132
133 //--------------------------------------------------------------------------------------------
134 //--------------------------------------------------------------------------------------------
unlink_enchant(const ENC_REF ienc,ENC_REF * enc_parent)135 bool_t unlink_enchant( const ENC_REF ienc, ENC_REF * enc_parent )
136 {
137 enc_t * penc;
138
139 if ( !ALLOCATED_ENC( ienc ) ) return bfalse;
140 penc = EncList.lst + ienc;
141
142 // Unlink it from the spawner (if possible)
143 if ( ALLOCATED_CHR( penc->spawner_ref ) )
144 {
145 chr_t * pspawner = ChrList.lst + penc->spawner_ref;
146
147 if ( ienc == pspawner->undoenchant )
148 {
149 pspawner->undoenchant = ( ENC_REF ) MAX_ENC;
150 }
151 }
152
153 // find the parent reference for the enchant
154 if ( NULL == enc_parent && ALLOCATED_CHR( penc->target_ref ) )
155 {
156 ENC_REF ienc_last, ienc_now, ienc_nxt;
157 size_t ienc_count;
158
159 chr_t * ptarget;
160
161 ptarget = ChrList.lst + penc->target_ref;
162
163 if ( ptarget->firstenchant == ienc )
164 {
165 // It was the first in the list
166 enc_parent = &( ptarget->firstenchant );
167 }
168 else
169 {
170 // Search until we find it
171 ienc_last = ienc_now = ptarget->firstenchant;
172 ienc_count = 0;
173 while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
174 {
175 ienc_last = ienc_now;
176 ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
177
178 if ( ienc_now == ienc ) break;
179
180 ienc_now = ienc_nxt;
181 ienc_count++;
182 }
183 if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
184
185 // Relink the last enchantment
186 if ( ienc_now == ienc )
187 {
188 enc_parent = &( EncList.lst[ienc_last].nextenchant_ref );
189 }
190 }
191 }
192
193 // unlink the enchant from the parent reference
194 if ( NULL != enc_parent )
195 {
196 *enc_parent = EncList.lst[ienc].nextenchant_ref;
197 }
198
199 return NULL != enc_parent;
200 }
201
202 //--------------------------------------------------------------------------------------------
remove_all_enchants_with_idsz(CHR_REF ichr,IDSZ remove_idsz)203 bool_t remove_all_enchants_with_idsz( CHR_REF ichr, IDSZ remove_idsz )
204 {
205 /// @details ZF@> This function removes all enchants with the character that has the specified
206 /// IDSZ. If idsz [NONE] is specified, all enchants will be removed. Return btrue
207 /// if at least one enchant was removed.
208
209 ENC_REF ienc_now, ienc_nxt;
210 size_t ienc_count;
211
212 eve_t * peve;
213 bool_t retval = bfalse;
214 chr_t *pchr;
215
216 // Stop invalid pointers
217 if ( !ACTIVE_CHR( ichr ) ) return bfalse;
218 pchr = ChrList.lst + ichr;
219
220 // clean up the enchant list before doing anything
221 cleanup_character_enchants( pchr );
222
223 // Check all enchants to see if they are removed
224 ienc_now = pchr->firstenchant;
225 ienc_count = 0;
226 while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
227 {
228 ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
229
230 peve = enc_get_peve( ienc_now );
231 if ( NULL != peve && ( IDSZ_NONE == remove_idsz || remove_idsz == peve->removedbyidsz ) )
232 {
233 remove_enchant( ienc_now, NULL );
234 retval = btrue;
235 }
236
237 ienc_now = ienc_nxt;
238 ienc_count++;
239 }
240 if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
241
242 return retval;
243 }
244
245 //--------------------------------------------------------------------------------------------
remove_enchant(const ENC_REF ienc,ENC_REF * enc_parent)246 bool_t remove_enchant( const ENC_REF ienc, ENC_REF * enc_parent )
247 {
248 /// @details ZZ@> This function removes a specific enchantment and adds it to the unused list
249
250 int iwave;
251 CHR_REF overlay_ref;
252 int add_type, set_type;
253
254 enc_t * penc;
255 eve_t * peve;
256 CHR_REF itarget, ispawner;
257
258 if ( !ALLOCATED_ENC( ienc ) ) return bfalse;
259 penc = EncList.lst + ienc;
260
261 itarget = penc->target_ref;
262 ispawner = penc->spawner_ref;
263 peve = enc_get_peve( ienc );
264
265 // Unsparkle the spellbook
266 if ( INGAME_CHR( ispawner ) )
267 {
268 chr_t * pspawner = ChrList.lst + ispawner;
269
270 pspawner->sparkle = NOSPARKLE;
271
272 // Make the spawner unable to undo the enchantment
273 if ( pspawner->undoenchant == ienc )
274 {
275 pspawner->undoenchant = ( ENC_REF ) MAX_ENC;
276 }
277 }
278
279 //---- Remove all the enchant stuff in exactly the opposite order to how it was applied
280
281 // Remove all of the cumulative values first, since we did it
282 for ( add_type = ENC_ADD_LAST; add_type >= ENC_ADD_FIRST; add_type-- )
283 {
284 enchant_remove_add( ienc, add_type );
285 }
286
287 // unset them in the reverse order of setting them, doing morph last
288 for ( set_type = ENC_SET_LAST; set_type >= ENC_SET_FIRST; set_type-- )
289 {
290 enchant_remove_set( ienc, set_type );
291 }
292
293 // Now fix dem weapons
294 if ( INGAME_CHR( itarget ) )
295 {
296 chr_t * ptarget = ChrList.lst + itarget;
297 reset_character_alpha( ptarget->holdingwhich[SLOT_LEFT] );
298 reset_character_alpha( ptarget->holdingwhich[SLOT_RIGHT] );
299 }
300
301 // unlink this enchant from its parent
302 unlink_enchant( ienc, enc_parent );
303
304 // Kill overlay too...
305 overlay_ref = penc->overlay_ref;
306 if ( INGAME_CHR( overlay_ref ) )
307 {
308 chr_t * povl = ChrList.lst + overlay_ref;
309
310 if ( povl->invictus )
311 {
312 chr_get_pteam_base( overlay_ref )->morale++;
313 }
314
315 kill_character( overlay_ref, ( CHR_REF )MAX_CHR, btrue );
316 }
317
318 // nothing above this demends on having a valid enchant profile
319 if ( NULL != peve )
320 {
321 // Play the end sound
322 iwave = peve->endsound_index;
323 if ( VALID_SND( iwave ) )
324 {
325 PRO_REF imodel = penc->spawnermodel_ref;
326 if ( LOADED_PRO( imodel ) )
327 {
328 if ( INGAME_CHR( itarget ) )
329 {
330 sound_play_chunk( ChrList.lst[itarget].pos_old, pro_get_chunk( imodel, iwave ) );
331 }
332 else
333 {
334 sound_play_chunk_full( pro_get_chunk( imodel, iwave ) );
335 }
336 }
337 }
338
339 // See if we spit out an end message
340 if ( peve->endmessage >= 0 )
341 {
342 _display_message( penc->target_ref, penc->profile_ref, peve->endmessage, NULL );
343 }
344
345 // Check to see if we spawn a poof
346 if ( peve->poofonend )
347 {
348 spawn_poof( penc->target_ref, penc->profile_ref );
349 }
350
351 //Remove special skills gained by the enchant
352 if ( INGAME_CHR( itarget ) )
353 {
354 chr_t * ptarget = ChrList.lst + penc->target_ref;
355
356 //Reset see kurses
357 if ( 0 != peve->seekurse )
358 {
359 ptarget->see_kurse_level = chr_get_skill( ptarget, MAKE_IDSZ( 'C', 'K', 'U', 'R' ) );
360 }
361
362 //Reset darkvision
363 if ( 0 != peve->darkvision )
364 {
365 ptarget->darkvision_level = chr_get_skill( ptarget, MAKE_IDSZ( 'D', 'A', 'R', 'K' ) );
366 }
367 }
368 }
369
370 EncList_free_one( ienc );
371
372 // save this until the enchant is completely dead, since kill character can generate a
373 // recursive call to this function through cleanup_one_character()
374 // @note all of the values in the penc are now invalid. we have to use previously evaluated
375 // values of itarget and penc to kill the target (if necessary)
376 if ( INGAME_CHR( itarget ) && NULL != peve && peve->killtargetonend )
377 {
378 chr_t * ptarget = ChrList.lst + itarget;
379 if ( ptarget->invictus ) chr_get_pteam_base( itarget )->morale++;
380
381 kill_character( itarget, ( CHR_REF )MAX_CHR, btrue );
382 }
383
384 return btrue;
385 }
386
387 //--------------------------------------------------------------------------------------------
enchant_value_filled(const ENC_REF ienc,int value_idx)388 ENC_REF enchant_value_filled( const ENC_REF ienc, int value_idx )
389 {
390 /// @details ZZ@> This function returns MAX_ENC if the enchantment's target has no conflicting
391 /// set values in its other enchantments. Otherwise it returns the ienc
392 /// of the conflicting enchantment
393
394 CHR_REF character;
395 chr_t * pchr;
396
397 ENC_REF ienc_now, ienc_nxt;
398 size_t ienc_count;
399
400 if ( value_idx < 0 || value_idx >= MAX_ENCHANT_SET ) return ( ENC_REF )MAX_ENC;
401
402 if ( !INGAME_ENC( ienc ) ) return ( ENC_REF )MAX_ENC;
403
404 character = EncList.lst[ienc].target_ref;
405 if ( !INGAME_CHR( character ) ) return ( ENC_REF )MAX_ENC;
406 pchr = ChrList.lst + character;
407
408 // cleanup the enchant list
409 cleanup_character_enchants( pchr );
410
411 // scan the enchant list
412 ienc_now = pchr->firstenchant;
413 ienc_count = 0;
414 while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
415 {
416 ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
417
418 if ( INGAME_ENC( ienc_now ) && EncList.lst[ienc_now].setyesno[value_idx] )
419 {
420 break;
421 }
422
423 ienc_now = ienc_nxt;
424 ienc_count++;
425 }
426 if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
427
428 return ienc_now;
429 }
430
431 //--------------------------------------------------------------------------------------------
enchant_apply_set(const ENC_REF ienc,int value_idx,const PRO_REF profile)432 void enchant_apply_set( const ENC_REF ienc, int value_idx, const PRO_REF profile )
433 {
434 /// @details ZZ@> This function sets and saves one of the character's stats
435
436 ENC_REF conflict;
437 CHR_REF character;
438 enc_t * penc;
439 eve_t * peve;
440 chr_t * ptarget;
441
442 if ( value_idx < 0 || value_idx >= MAX_ENCHANT_SET ) return;
443
444 if ( !DEFINED_ENC( ienc ) ) return;
445 penc = EncList.lst + ienc;
446
447 peve = pro_get_peve( profile );
448 if ( NULL == peve ) return;
449
450 penc->setyesno[value_idx] = bfalse;
451 if ( peve->setyesno[value_idx] )
452 {
453 conflict = enchant_value_filled( ienc, value_idx );
454 if ( peve->override || MAX_ENC == conflict )
455 {
456 // Check for multiple enchantments
457 if ( DEFINED_ENC( conflict ) )
458 {
459 // Multiple enchantments aren't allowed for sets
460 if ( peve->remove_overridden )
461 {
462 // Kill the old enchantment
463 remove_enchant( conflict, NULL );
464 }
465 else
466 {
467 // Just unset the old enchantment's value
468 enchant_remove_set( conflict, value_idx );
469 }
470 }
471
472 // Set the value, and save the character's real stat
473 if ( DEFINED_CHR( penc->target_ref ) )
474 {
475 character = penc->target_ref;
476 ptarget = ChrList.lst + character;
477
478 penc->setyesno[value_idx] = btrue;
479
480 switch ( value_idx )
481 {
482 case SETDAMAGETYPE:
483 penc->setsave[value_idx] = ptarget->damagetarget_damagetype;
484 ptarget->damagetarget_damagetype = peve->setvalue[value_idx];
485 break;
486
487 case SETNUMBEROFJUMPS:
488 penc->setsave[value_idx] = ptarget->jumpnumberreset;
489 ptarget->jumpnumberreset = peve->setvalue[value_idx];
490 break;
491
492 case SETLIFEBARCOLOR:
493 penc->setsave[value_idx] = ptarget->lifecolor;
494 ptarget->lifecolor = peve->setvalue[value_idx];
495 break;
496
497 case SETMANABARCOLOR:
498 penc->setsave[value_idx] = ptarget->manacolor;
499 ptarget->manacolor = peve->setvalue[value_idx];
500 break;
501
502 case SETSLASHMODIFIER:
503 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_SLASH];
504 ptarget->damage_modifier[DAMAGE_SLASH] = peve->setvalue[value_idx];
505 break;
506
507 case SETCRUSHMODIFIER:
508 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_CRUSH];
509 ptarget->damage_modifier[DAMAGE_CRUSH] = peve->setvalue[value_idx];
510 break;
511
512 case SETPOKEMODIFIER:
513 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_POKE];
514 ptarget->damage_modifier[DAMAGE_POKE] = peve->setvalue[value_idx];
515 break;
516
517 case SETHOLYMODIFIER:
518 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_HOLY];
519 ptarget->damage_modifier[DAMAGE_HOLY] = peve->setvalue[value_idx];
520 break;
521
522 case SETEVILMODIFIER:
523 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_EVIL];
524 ptarget->damage_modifier[DAMAGE_EVIL] = peve->setvalue[value_idx];
525 break;
526
527 case SETFIREMODIFIER:
528 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_FIRE];
529 ptarget->damage_modifier[DAMAGE_FIRE] = peve->setvalue[value_idx];
530 break;
531
532 case SETICEMODIFIER:
533 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_ICE];
534 ptarget->damage_modifier[DAMAGE_ICE] = peve->setvalue[value_idx];
535 break;
536
537 case SETZAPMODIFIER:
538 penc->setsave[value_idx] = ptarget->damage_modifier[DAMAGE_ZAP];
539 ptarget->damage_modifier[DAMAGE_ZAP] = peve->setvalue[value_idx];
540 break;
541
542 case SETFLASHINGAND:
543 penc->setsave[value_idx] = ptarget->flashand;
544 ptarget->flashand = peve->setvalue[value_idx];
545 break;
546
547 case SETLIGHTBLEND:
548 penc->setsave[value_idx] = ptarget->inst.light;
549 chr_set_light( ptarget, peve->setvalue[value_idx] );
550 break;
551
552 case SETALPHABLEND:
553 penc->setsave[value_idx] = ptarget->inst.alpha;
554 chr_set_alpha( ptarget, peve->setvalue[value_idx] );
555 break;
556
557 case SETSHEEN:
558 penc->setsave[value_idx] = ptarget->inst.sheen;
559 chr_set_sheen( ptarget, peve->setvalue[value_idx] );
560 break;
561
562 case SETFLYTOHEIGHT:
563 penc->setsave[value_idx] = ptarget->flyheight;
564 if ( 0 == ptarget->flyheight && ptarget->pos.z > -2 )
565 {
566 ptarget->flyheight = peve->setvalue[value_idx];
567 }
568 break;
569
570 case SETWALKONWATER:
571 penc->setsave[value_idx] = ptarget->waterwalk;
572 if ( !ptarget->waterwalk )
573 {
574 ptarget->waterwalk = ( 0 != peve->setvalue[value_idx] );
575 }
576 break;
577
578 case SETCANSEEINVISIBLE:
579 penc->setsave[value_idx] = ptarget->see_invisible_level > 0;
580 ptarget->see_invisible_level = peve->setvalue[value_idx];
581 break;
582
583 case SETMISSILETREATMENT:
584 penc->setsave[value_idx] = ptarget->missiletreatment;
585 ptarget->missiletreatment = peve->setvalue[value_idx];
586 break;
587
588 case SETCOSTFOREACHMISSILE:
589 penc->setsave[value_idx] = ptarget->missilecost;
590 ptarget->missilecost = peve->setvalue[value_idx] * 16.0f; // adjustment to the value stored in the file
591 ptarget->missilehandler = penc->owner_ref;
592 break;
593
594 case SETMORPH:
595 // Special handler for morph
596 penc->setsave[value_idx] = ptarget->skin;
597 change_character( character, profile, 0, ENC_LEAVE_ALL ); // ENC_LEAVE_FIRST);
598 break;
599
600 case SETCHANNEL:
601 penc->setsave[value_idx] = ptarget->canchannel;
602 ptarget->canchannel = ( 0 != peve->setvalue[value_idx] );
603 break;
604 }
605 }
606 }
607 }
608 }
609
610 //--------------------------------------------------------------------------------------------
enchant_apply_add(const ENC_REF ienc,int value_idx,const EVE_REF ieve)611 void enchant_apply_add( const ENC_REF ienc, int value_idx, const EVE_REF ieve )
612 {
613 /// @details ZZ@> This function does cumulative modification to character stats
614
615 int valuetoadd, newvalue;
616 float fvaluetoadd, fnewvalue;
617 CHR_REF character;
618 enc_t * penc;
619 eve_t * peve;
620 chr_t * ptarget;
621
622 if ( value_idx < 0 || value_idx >= MAX_ENCHANT_ADD ) return;
623
624 if ( !DEFINED_ENC( ienc ) ) return;
625 penc = EncList.lst + ienc;
626
627 if ( ieve >= MAX_EVE || !EveStack.lst[ieve].loaded ) return;
628 peve = EveStack.lst + ieve;
629
630 if ( !peve->addyesno[value_idx] )
631 {
632 penc->addyesno[value_idx] = bfalse;
633 penc->addsave[value_idx] = 0.0f;
634 return;
635 }
636
637 if ( !DEFINED_CHR( penc->target_ref ) ) return;
638 character = penc->target_ref;
639 ptarget = ChrList.lst + character;
640
641 valuetoadd = 0;
642 fvaluetoadd = 0.0f;
643 switch ( value_idx )
644 {
645 case ADDJUMPPOWER:
646 fnewvalue = ptarget->jump_power;
647 fvaluetoadd = peve->addvalue[value_idx];
648 fgetadd( 0.0f, fnewvalue, 30.0f, &fvaluetoadd );
649 ptarget->jump_power += fvaluetoadd;
650 break;
651
652 case ADDBUMPDAMPEN:
653 fnewvalue = ptarget->phys.bumpdampen;
654 fvaluetoadd = peve->addvalue[value_idx];
655 fgetadd( 0.0f, fnewvalue, 1.0f, &fvaluetoadd );
656 ptarget->phys.bumpdampen += fvaluetoadd;
657 break;
658
659 case ADDBOUNCINESS:
660 fnewvalue = ptarget->phys.dampen;
661 fvaluetoadd = peve->addvalue[value_idx];
662 fgetadd( 0.0f, fnewvalue, 0.95f, &fvaluetoadd );
663 ptarget->phys.dampen += fvaluetoadd;
664 break;
665
666 case ADDDAMAGE:
667 newvalue = ptarget->damage_boost;
668 valuetoadd = peve->addvalue[value_idx];
669 getadd( 0, newvalue, 4096, &valuetoadd );
670 ptarget->damage_boost += valuetoadd;
671 fvaluetoadd = valuetoadd;
672 break;
673
674 case ADDSIZE:
675 fnewvalue = ptarget->fat_goto;
676 fvaluetoadd = peve->addvalue[value_idx];
677 fgetadd( 0.5f, fnewvalue, 2.0f, &fvaluetoadd );
678 ptarget->fat_goto += fvaluetoadd;
679 ptarget->fat_goto_time = SIZETIME;
680 break;
681
682 case ADDACCEL:
683 fnewvalue = ptarget->maxaccel_reset;
684 fvaluetoadd = peve->addvalue[value_idx];
685 fgetadd( 0.0f, fnewvalue, 1.50f, &fvaluetoadd );
686 chr_set_maxaccel( ptarget, ptarget->maxaccel_reset + fvaluetoadd );
687 break;
688
689 case ADDRED:
690 newvalue = ptarget->inst.redshift;
691 valuetoadd = peve->addvalue[value_idx];
692 getadd( 0, newvalue, 6, &valuetoadd );
693 chr_set_redshift( ptarget, ptarget->inst.redshift + valuetoadd );
694 fvaluetoadd = valuetoadd;
695 break;
696
697 case ADDGRN:
698 newvalue = ptarget->inst.grnshift;
699 valuetoadd = peve->addvalue[value_idx];
700 getadd( 0, newvalue, 6, &valuetoadd );
701 chr_set_grnshift( ptarget, ptarget->inst.grnshift + valuetoadd );
702 fvaluetoadd = valuetoadd;
703 break;
704
705 case ADDBLU:
706 newvalue = ptarget->inst.blushift;
707 valuetoadd = peve->addvalue[value_idx];
708 getadd( 0, newvalue, 6, &valuetoadd );
709 chr_set_blushift( ptarget, ptarget->inst.blushift + valuetoadd );
710 fvaluetoadd = valuetoadd;
711 break;
712
713 case ADDDEFENSE:
714 newvalue = ptarget->defense;
715 valuetoadd = peve->addvalue[value_idx];
716 getadd( 55, newvalue, 255, &valuetoadd ); // Don't fix again! //ZF> why limit min to 55?
717 ptarget->defense += valuetoadd;
718 fvaluetoadd = valuetoadd;
719 break;
720
721 case ADDMANA:
722 newvalue = ptarget->manamax;
723 valuetoadd = peve->addvalue[value_idx];
724 getadd( 0, newvalue, PERFECTBIG, &valuetoadd );
725 ptarget->manamax += valuetoadd;
726 //ptarget->mana += valuetoadd; //ZF> bit of a problem here, we dont want players to heal or lose life by requipping magic ornaments
727 ptarget->mana = CLIP( ptarget->mana, 0, ptarget->manamax );
728 fvaluetoadd = valuetoadd;
729 break;
730
731 case ADDLIFE:
732 newvalue = ptarget->lifemax;
733 valuetoadd = peve->addvalue[value_idx];
734 getadd( LOWSTAT, newvalue, PERFECTBIG, &valuetoadd );
735 ptarget->lifemax += valuetoadd;
736 //ptarget->life += valuetoadd; //ZF> bit of a problem here, we dont want players to heal or lose life by requipping magic ornaments
737 ptarget->life = CLIP( ptarget->life, 1, ptarget->lifemax );
738 fvaluetoadd = valuetoadd;
739 break;
740
741 case ADDSTRENGTH:
742 newvalue = ptarget->strength;
743 valuetoadd = peve->addvalue[value_idx];
744 getadd( 0, newvalue, HIGHSTAT, &valuetoadd );
745 ptarget->strength += valuetoadd;
746 fvaluetoadd = valuetoadd;
747 break;
748
749 case ADDWISDOM:
750 newvalue = ptarget->wisdom;
751 valuetoadd = peve->addvalue[value_idx];
752 getadd( 0, newvalue, HIGHSTAT, &valuetoadd );
753 ptarget->wisdom += valuetoadd;
754 fvaluetoadd = valuetoadd;
755 break;
756
757 case ADDINTELLIGENCE:
758 newvalue = ptarget->intelligence;
759 valuetoadd = peve->addvalue[value_idx];
760 getadd( 0, newvalue, HIGHSTAT, &valuetoadd );
761 ptarget->intelligence += valuetoadd;
762 fvaluetoadd = valuetoadd;
763 break;
764
765 case ADDDEXTERITY:
766 newvalue = ptarget->dexterity;
767 valuetoadd = peve->addvalue[value_idx];
768 getadd( 0, newvalue, HIGHSTAT, &valuetoadd );
769 ptarget->dexterity += valuetoadd;
770 fvaluetoadd = valuetoadd;
771 break;
772 }
773
774 // save whether there was any change in the value
775 penc->addyesno[value_idx] = ( 0.0f != fvaluetoadd );
776
777 // Save the value for undo
778 penc->addsave[value_idx] = fvaluetoadd;
779 }
780
781 //--------------------------------------------------------------------------------------------
782 //--------------------------------------------------------------------------------------------
enc_config_do_init(enc_t * penc)783 enc_t * enc_config_do_init( enc_t * penc )
784 {
785 enc_spawn_data_t * pdata;
786 ENC_REF ienc;
787 CHR_REF overlay;
788
789 eve_t * peve;
790 chr_t * ptarget;
791
792 int add_type, set_type;
793
794 if ( NULL == penc ) return NULL;
795 ienc = GET_INDEX_PENC( penc );
796
797 // get the profile data
798 pdata = &( penc->spawn_data );
799
800 // store the profile
801 penc->profile_ref = pdata->profile_ref;
802
803 // Convert from local pdata->eve_ref to global enchant profile
804 if ( !LOADED_EVE( pdata->eve_ref ) )
805 {
806 log_debug( "spawn_one_enchant() - cannot spawn enchant with invalid enchant template (\"eve\") == %d\n", REF_TO_INT( pdata->eve_ref ) );
807
808 return NULL;
809 }
810 penc->eve_ref = pdata->eve_ref;
811 peve = EveStack.lst + pdata->eve_ref;
812
813 // turn the enchant on here. you can't fail to spawn after this point.
814 POBJ_ACTIVATE( penc, peve->name );
815
816 // does the target exist?
817 if ( !DEFINED_CHR( pdata->target_ref ) )
818 {
819 penc->target_ref = ( CHR_REF )MAX_CHR;
820 ptarget = NULL;
821 }
822 else
823 {
824 penc->target_ref = pdata->target_ref;
825 ptarget = ChrList.lst + penc->target_ref;
826 }
827 penc->target_mana = peve->target_mana;
828 penc->target_life = peve->target_life;
829
830 // does the owner exist?
831 if ( !DEFINED_CHR( pdata->owner_ref ) )
832 {
833 penc->owner_ref = ( CHR_REF )MAX_CHR;
834 }
835 else
836 {
837 penc->owner_ref = pdata->owner_ref;
838 }
839 penc->owner_mana = peve->owner_mana;
840 penc->owner_life = peve->owner_life;
841
842 // does the spawner exist?
843 if ( !DEFINED_CHR( pdata->spawner_ref ) )
844 {
845 penc->spawner_ref = ( CHR_REF )MAX_CHR;
846 penc->spawnermodel_ref = ( PRO_REF )MAX_PROFILE;
847 }
848 else
849 {
850 penc->spawner_ref = pdata->spawner_ref;
851 penc->spawnermodel_ref = chr_get_ipro( pdata->spawner_ref );
852
853 ChrList.lst[penc->spawner_ref].undoenchant = ienc;
854 }
855
856 // set some other spawning parameters
857 penc->lifetime = peve->lifetime;
858 penc->spawn_timer = 1;
859
860 // Now set all of the specific values, morph first
861 for ( set_type = ENC_SET_FIRST; set_type <= ENC_SET_LAST; set_type++ )
862 {
863 enchant_apply_set( ienc, set_type, pdata->profile_ref );
864 }
865
866 // Now do all of the stat adds
867 for ( add_type = ENC_ADD_FIRST; add_type <= ENC_ADD_LAST; add_type++ )
868 {
869 enchant_apply_add( ienc, add_type, pdata->eve_ref );
870 }
871
872 // Add it as first in the list
873 if ( NULL != ptarget )
874 {
875 penc->nextenchant_ref = ptarget->firstenchant;
876 ptarget->firstenchant = ienc;
877 }
878
879 // Create an overlay character?
880 if ( peve->spawn_overlay && NULL != ptarget )
881 {
882 overlay = spawn_one_character( ptarget->pos, pdata->profile_ref, ptarget->team, 0, ptarget->ori.facing_z, NULL, ( CHR_REF )MAX_CHR );
883 if ( DEFINED_CHR( overlay ) )
884 {
885 chr_t * povl;
886 mad_t * povl_mad;
887 int action;
888
889 povl = ChrList.lst + overlay;
890 povl_mad = chr_get_pmad( overlay );
891
892 penc->overlay_ref = overlay; // Kill this character on end...
893 povl->ai.target = pdata->target_ref;
894 povl->is_overlay = btrue;
895 chr_set_ai_state( povl, peve->spawn_overlay ); // ??? WHY DO THIS ???
896
897 // Start out with ActionMJ... Object activated
898 action = mad_get_action_ref( chr_get_imad( overlay ), ACTION_MJ );
899 if ( !ACTION_IS_TYPE( action, D ) )
900 {
901 chr_start_anim( povl, action, bfalse, btrue );
902 }
903
904 // Assume it's transparent...
905 chr_set_light( povl, 254 );
906 chr_set_alpha( povl, 0 );
907 }
908 }
909
910 //Apply special skill effects
911 if ( NULL != ptarget )
912 {
913
914 // Allow them to see kurses?
915 if ( 0 != peve->seekurse )
916 {
917 ptarget->see_kurse_level = peve->seekurse;
918 }
919
920 // Allow them to see in darkness (or blindness if negative)
921 if ( 0 != peve->darkvision )
922 {
923 ptarget->darkvision_level = peve->darkvision;
924 }
925
926 }
927
928 return penc;
929 }
930
931 //--------------------------------------------------------------------------------------------
enc_config_do_active(enc_t * penc)932 enc_t * enc_config_do_active( enc_t * penc )
933 {
934 /// @details ZZ@> This function allows enchantments to update, spawn particles,
935 // do drains, stat boosts and despawn.
936
937 ENC_REF ienc;
938 CHR_REF owner, target;
939 EVE_REF eve;
940 eve_t * peve;
941 chr_t * ptarget;
942
943 if ( NULL == penc ) return penc;
944 ienc = GET_REF_PENC( penc );
945
946 // the following functions should not be done the first time through the update loop
947 if ( 0 == clock_wld ) return penc;
948
949 peve = enc_get_peve( ienc );
950 if ( NULL == peve ) return penc;
951
952 // check to see whether the enchant needs to spawn some particles
953 if ( penc->spawn_timer > 0 ) penc->spawn_timer--;
954
955 if ( 0 == penc->spawn_timer && peve->contspawn_amount <= 0 )
956 {
957 int tnc;
958 FACING_T facing;
959 penc->spawn_timer = peve->contspawn_delay;
960 ptarget = ChrList.lst + penc->target_ref;
961
962 facing = ptarget->ori.facing_z;
963 for ( tnc = 0; tnc < peve->contspawn_amount; tnc++ )
964 {
965 spawn_one_particle( ptarget->pos, facing, penc->profile_ref, peve->contspawn_lpip,
966 ( CHR_REF )MAX_CHR, GRIP_LAST, chr_get_iteam( penc->owner_ref ), penc->owner_ref, ( PRT_REF )MAX_PRT, tnc, ( CHR_REF )MAX_CHR );
967
968 facing += peve->contspawn_facingadd;
969 }
970 }
971
972 // Do enchant drains and regeneration
973 if ( clock_enc_stat >= ONESECOND )
974 {
975 if ( 0 == penc->lifetime )
976 {
977 enc_request_terminate( ienc );
978 }
979 else
980 {
981 // Do enchant timer
982 if ( penc->lifetime > 0 ) penc->lifetime--;
983
984 // To make life easier
985 owner = enc_get_iowner( ienc );
986 target = penc->target_ref;
987 eve = enc_get_ieve( ienc );
988
989 // Do drains
990 if ( ChrList.lst[owner].alive )
991 {
992
993 // Change life
994 if ( 0 != penc->owner_life )
995 {
996 ChrList.lst[owner].life += penc->owner_life;
997 if ( ChrList.lst[owner].life <= 0 )
998 {
999 kill_character( owner, target, bfalse );
1000 }
1001 if ( ChrList.lst[owner].life > ChrList.lst[owner].lifemax )
1002 {
1003 ChrList.lst[owner].life = ChrList.lst[owner].lifemax;
1004 }
1005 }
1006
1007 // Change mana
1008 if ( 0 != penc->owner_mana )
1009 {
1010 bool_t mana_paid = cost_mana( owner, -penc->owner_mana, target );
1011 if ( EveStack.lst[eve].endifcantpay && !mana_paid )
1012 {
1013 enc_request_terminate( ienc );
1014 }
1015 }
1016
1017 }
1018 else if ( !EveStack.lst[eve].stayifnoowner )
1019 {
1020 enc_request_terminate( ienc );
1021 }
1022
1023 // the enchant could have been inactivated by the stuff above
1024 // check it again
1025 if ( INGAME_ENC( ienc ) )
1026 {
1027 if ( ChrList.lst[target].alive )
1028 {
1029
1030 // Change life
1031 if ( 0 != penc->target_life )
1032 {
1033 ChrList.lst[target].life += penc->target_life;
1034 if ( ChrList.lst[target].life <= 0 )
1035 {
1036 kill_character( target, owner, bfalse );
1037 }
1038 if ( ChrList.lst[target].life > ChrList.lst[target].lifemax )
1039 {
1040 ChrList.lst[target].life = ChrList.lst[target].lifemax;
1041 }
1042 }
1043
1044 // Change mana
1045 if ( 0 != penc->target_mana )
1046 {
1047 bool_t mana_paid = cost_mana( target, -penc->target_mana, owner );
1048 if ( EveStack.lst[eve].endifcantpay && !mana_paid )
1049 {
1050 enc_request_terminate( ienc );
1051 }
1052 }
1053
1054 }
1055 else if ( !EveStack.lst[eve].stayiftargetdead )
1056 {
1057 enc_request_terminate( ienc );
1058 }
1059 }
1060 }
1061 }
1062
1063 return penc;
1064 }
1065
1066 //--------------------------------------------------------------------------------------------
1067 //--------------------------------------------------------------------------------------------
enc_config_construct(enc_t * penc,int max_iterations)1068 enc_t * enc_config_construct( enc_t * penc, int max_iterations )
1069 {
1070 int iterations;
1071 obj_data_t * pbase;
1072
1073 if ( NULL == penc ) return NULL;
1074
1075 pbase = POBJ_GET_PBASE( penc );
1076 if ( !pbase->allocated ) return NULL;
1077
1078 // if the enchant is already beyond this stage, deconstruct it and start over
1079 if ( pbase->state > ( int )( ego_object_constructing + 1 ) )
1080 {
1081 enc_t * tmp_enc = enc_config_deconstruct( penc, max_iterations );
1082 if ( tmp_enc == penc ) return NULL;
1083 }
1084
1085 iterations = 0;
1086 while ( NULL != penc && pbase->state <= ego_object_constructing && iterations < max_iterations )
1087 {
1088 enc_t * ptmp = enc_run_config( penc );
1089 if ( ptmp != penc ) return NULL;
1090 iterations++;
1091 }
1092
1093 return penc;
1094 }
1095
1096 //--------------------------------------------------------------------------------------------
enc_config_initialize(enc_t * penc,int max_iterations)1097 enc_t * enc_config_initialize( enc_t * penc, int max_iterations )
1098 {
1099 int iterations;
1100 obj_data_t * pbase;
1101
1102 if ( NULL == penc ) return NULL;
1103
1104 pbase = POBJ_GET_PBASE( penc );
1105 if ( !pbase->allocated ) return NULL;
1106
1107 // if the enchant is already beyond this stage, deconstruct it and start over
1108 if ( pbase->state > ( int )( ego_object_initializing + 1 ) )
1109 {
1110 enc_t * tmp_enc = enc_config_deconstruct( penc, max_iterations );
1111 if ( tmp_enc == penc ) return NULL;
1112 }
1113
1114 iterations = 0;
1115 while ( NULL != penc && pbase->state <= ego_object_initializing && iterations < max_iterations )
1116 {
1117 enc_t * ptmp = enc_run_config( penc );
1118 if ( ptmp != penc ) return NULL;
1119 iterations++;
1120 }
1121
1122 return penc;
1123 }
1124
1125 //--------------------------------------------------------------------------------------------
enc_config_activate(enc_t * penc,int max_iterations)1126 enc_t * enc_config_activate( enc_t * penc, int max_iterations )
1127 {
1128 int iterations;
1129 obj_data_t * pbase;
1130
1131 if ( NULL == penc ) return NULL;
1132
1133 pbase = POBJ_GET_PBASE( penc );
1134 if ( !pbase->allocated ) return NULL;
1135
1136 // if the particle is already beyond this stage, deconstruct it and start over
1137 if ( pbase->state > ( int )( ego_object_active + 1 ) )
1138 {
1139 enc_t * tmp_enc = enc_config_deconstruct( penc, max_iterations );
1140 if ( tmp_enc == penc ) return NULL;
1141 }
1142
1143 iterations = 0;
1144 while ( NULL != penc && pbase->state < ego_object_active && iterations < max_iterations )
1145 {
1146 enc_t * ptmp = enc_run_config( penc );
1147 if ( ptmp != penc ) return NULL;
1148 iterations++;
1149 }
1150
1151 EGOBOO_ASSERT( pbase->state == ego_object_active );
1152 if ( pbase->state == ego_object_active )
1153 {
1154 EncList_add_used( GET_INDEX_PENC( penc ) );
1155 }
1156
1157 return penc;
1158 }
1159
1160 //--------------------------------------------------------------------------------------------
enc_config_deinitialize(enc_t * penc,int max_iterations)1161 enc_t * enc_config_deinitialize( enc_t * penc, int max_iterations )
1162 {
1163 int iterations;
1164 obj_data_t * pbase;
1165
1166 if ( NULL == penc ) return NULL;
1167
1168 pbase = POBJ_GET_PBASE( penc );
1169 if ( !pbase->allocated ) return NULL;
1170
1171 // if the particle is already beyond this stage, deinitialize it
1172 if ( pbase->state > ( int )( ego_object_deinitializing + 1 ) )
1173 {
1174 return penc;
1175 }
1176 else if ( pbase->state < ego_object_deinitializing )
1177 {
1178 pbase->state = ego_object_deinitializing;
1179 }
1180
1181 iterations = 0;
1182 while ( NULL != penc && pbase->state <= ego_object_deinitializing && iterations < max_iterations )
1183 {
1184 enc_t * ptmp = enc_run_config( penc );
1185 if ( ptmp != penc ) return NULL;
1186 iterations++;
1187 }
1188
1189 return penc;
1190 }
1191
1192 //--------------------------------------------------------------------------------------------
enc_config_deconstruct(enc_t * penc,int max_iterations)1193 enc_t * enc_config_deconstruct( enc_t * penc, int max_iterations )
1194 {
1195 int iterations;
1196 obj_data_t * pbase;
1197
1198 if ( NULL == penc ) return NULL;
1199
1200 pbase = POBJ_GET_PBASE( penc );
1201 if ( !pbase->allocated ) return NULL;
1202
1203 // if the particle is already beyond this stage, deconstruct it
1204 if ( pbase->state > ( int )( ego_object_destructing + 1 ) )
1205 {
1206 return penc;
1207 }
1208 else if ( pbase->state < ego_object_deinitializing )
1209 {
1210 // make sure that you deinitialize before destructing
1211 pbase->state = ego_object_deinitializing;
1212 }
1213
1214 iterations = 0;
1215 while ( NULL != penc && pbase->state <= ego_object_destructing && iterations < max_iterations )
1216 {
1217 enc_t * ptmp = enc_run_config( penc );
1218 if ( ptmp != penc ) return NULL;
1219 iterations++;
1220 }
1221
1222 return penc;
1223 }
1224
1225 //--------------------------------------------------------------------------------------------
1226 //--------------------------------------------------------------------------------------------
enc_run_config(enc_t * penc)1227 enc_t * enc_run_config( enc_t * penc )
1228 {
1229 obj_data_t * pbase;
1230
1231 if ( NULL == penc ) return NULL;
1232
1233 pbase = POBJ_GET_PBASE( penc );
1234 if ( !pbase->allocated ) return NULL;
1235
1236 // set the object to deinitialize if it is not "dangerous" and if was requested
1237 if ( pbase->kill_me )
1238 {
1239 if ( pbase->state > ego_object_constructing && pbase->state < ego_object_deinitializing )
1240 {
1241 pbase->state = ego_object_deinitializing;
1242 }
1243
1244 pbase->kill_me = bfalse;
1245 }
1246
1247 switch ( pbase->state )
1248 {
1249 default:
1250 case ego_object_invalid:
1251 penc = NULL;
1252 break;
1253
1254 case ego_object_constructing:
1255 penc = enc_config_ctor( penc );
1256 break;
1257
1258 case ego_object_initializing:
1259 penc = enc_config_init( penc );
1260 break;
1261
1262 case ego_object_active:
1263 penc = enc_config_active( penc );
1264 break;
1265
1266 case ego_object_deinitializing:
1267 penc = enc_config_deinit( penc );
1268 break;
1269
1270 case ego_object_destructing:
1271 penc = enc_config_dtor( penc );
1272 break;
1273
1274 case ego_object_waiting:
1275 case ego_object_terminated:
1276 /* do nothing */
1277 break;
1278 }
1279
1280 if ( NULL == penc )
1281 {
1282 pbase->update_guid = INVALID_UPDATE_GUID;
1283 }
1284 else if ( ego_object_active == pbase->state )
1285 {
1286 pbase->update_guid = EncList.update_guid;
1287 }
1288
1289 return penc;
1290 }
1291
1292 //--------------------------------------------------------------------------------------------
enc_config_ctor(enc_t * penc)1293 enc_t * enc_config_ctor( enc_t * penc )
1294 {
1295 obj_data_t * pbase;
1296
1297 // grab the base object
1298 if ( NULL == penc ) return NULL;
1299 pbase = POBJ_GET_PBASE( penc );
1300
1301 // if we aren't in the correct state, abort.
1302 if ( !STATE_CONSTRUCTING_PBASE( pbase ) ) return penc;
1303
1304 return enc_ctor( penc );
1305 }
1306
1307 //--------------------------------------------------------------------------------------------
enc_config_init(enc_t * penc)1308 enc_t * enc_config_init( enc_t * penc )
1309 {
1310 obj_data_t * pbase;
1311
1312 if ( NULL == penc ) return NULL;
1313
1314 pbase = POBJ_GET_PBASE( penc );
1315 if ( !STATE_INITIALIZING_PBASE( pbase ) ) return penc;
1316
1317 penc = enc_config_do_init( penc );
1318 if ( NULL == penc ) return NULL;
1319
1320 if ( 0 == enc_loop_depth )
1321 {
1322 penc->obj_base.on = btrue;
1323 }
1324 else
1325 {
1326 EncList_add_activation( GET_INDEX_PENC( penc ) );
1327 }
1328
1329 pbase->state = ego_object_active;
1330
1331 return penc;
1332 }
1333
1334 //--------------------------------------------------------------------------------------------
enc_config_active(enc_t * penc)1335 enc_t * enc_config_active( enc_t * penc )
1336 {
1337 // there's nothing to configure if the object is active...
1338
1339 obj_data_t * pbase;
1340
1341 if ( NULL == penc ) return NULL;
1342
1343 pbase = POBJ_GET_PBASE( penc );
1344 if ( !pbase->allocated ) return NULL;
1345
1346 if ( !STATE_ACTIVE_PBASE( pbase ) ) return penc;
1347
1348 POBJ_END_SPAWN( penc );
1349
1350 penc = enc_config_do_active( penc );
1351
1352 return penc;
1353 }
1354
1355 //--------------------------------------------------------------------------------------------
enc_config_deinit(enc_t * penc)1356 enc_t * enc_config_deinit( enc_t * penc )
1357 {
1358 /// @details BB@> deinitialize the character data
1359
1360 obj_data_t * pbase;
1361
1362 if ( NULL == penc ) return NULL;
1363 pbase = POBJ_GET_PBASE( penc );
1364
1365 if ( !STATE_DEINITIALIZING_PBASE( pbase ) ) return penc;
1366
1367 POBJ_END_SPAWN( penc );
1368
1369 pbase->state = ego_object_destructing;
1370 pbase->on = bfalse;
1371
1372 return penc;
1373 }
1374
1375 //--------------------------------------------------------------------------------------------
enc_config_dtor(enc_t * penc)1376 enc_t * enc_config_dtor( enc_t * penc )
1377 {
1378 obj_data_t * pbase;
1379
1380 if ( NULL == penc ) return NULL;
1381 pbase = POBJ_GET_PBASE( penc );
1382
1383 if ( !STATE_DESTRUCTING_PBASE( pbase ) ) return penc;
1384
1385 POBJ_END_SPAWN( penc );
1386
1387 return enc_dtor( penc );
1388 }
1389
1390 //--------------------------------------------------------------------------------------------
spawn_one_enchant(const CHR_REF owner,const CHR_REF target,const CHR_REF spawner,const ENC_REF enc_override,const PRO_REF modeloptional)1391 ENC_REF spawn_one_enchant( const CHR_REF owner, const CHR_REF target, const CHR_REF spawner, const ENC_REF enc_override, const PRO_REF modeloptional )
1392 {
1393 /// @details ZZ@> This function enchants a target, returning the enchantment index or MAX_ENC
1394 /// if failed
1395
1396 ENC_REF enc_ref;
1397 EVE_REF eve_ref;
1398
1399 eve_t * peve;
1400 enc_t * penc;
1401 chr_t * ptarget;
1402
1403 PRO_REF loc_profile;
1404 CHR_REF loc_target;
1405
1406 // Target must both be alive and on and valid
1407 loc_target = target;
1408 if ( !DEFINED_CHR( loc_target ) )
1409 {
1410 log_warning( "spawn_one_enchant() - failed because the target does not exist.\n" );
1411 return ( ENC_REF )MAX_ENC;
1412 }
1413 ptarget = ChrList.lst + loc_target;
1414
1415 // you should be able to enchant dead stuff to raise the dead...
1416 // if( !ptarget->alive ) return (ENC_REF)MAX_ENC;
1417
1418 if ( LOADED_PRO( modeloptional ) )
1419 {
1420 // The enchantment type is given explicitly
1421 loc_profile = modeloptional;
1422 }
1423 else
1424 {
1425 // The enchantment type is given by the spawner
1426 loc_profile = chr_get_ipro( spawner );
1427
1428 if ( !LOADED_PRO( loc_profile ) )
1429 {
1430 log_warning( "spawn_one_enchant() - no valid profile for the spawning character \"%s\"(%d).\n", ChrList.lst[spawner].obj_base._name, REF_TO_INT( spawner ) );
1431 return ( ENC_REF )MAX_ENC;
1432 }
1433 }
1434
1435 eve_ref = pro_get_ieve( loc_profile );
1436 if ( !LOADED_EVE( eve_ref ) )
1437 {
1438 log_warning( "spawn_one_enchant() - the object \"%s\"(%d) does not have an enchant profile.\n", ProList.lst[loc_profile].name, REF_TO_INT( loc_profile ) );
1439
1440 return ( ENC_REF )MAX_ENC;
1441 }
1442 peve = EveStack.lst + eve_ref;
1443
1444 // count all the requests for this enchantment type
1445 peve->request_count++;
1446
1447 // Owner must both be alive and on and valid if it isn't a stayifnoowner enchant
1448 if ( !peve->stayifnoowner && ( !INGAME_CHR( owner ) || !ChrList.lst[owner].alive ) )
1449 {
1450 log_warning( "spawn_one_enchant() - failed because the required enchant owner cannot be found.\n" );
1451 return ( ENC_REF )MAX_ENC;
1452 }
1453
1454 // do retargeting, if necessary
1455 // Should it choose an inhand item?
1456 if ( peve->retarget )
1457 {
1458 // Left, right, or both are valid
1459 if ( INGAME_CHR( ptarget->holdingwhich[SLOT_LEFT] ) )
1460 {
1461 // Only right hand is valid
1462 loc_target = ptarget->holdingwhich[SLOT_RIGHT];
1463 }
1464 else if ( INGAME_CHR( ptarget->holdingwhich[SLOT_LEFT] ) )
1465 {
1466 // Pick left hand
1467 loc_target = ptarget->holdingwhich[SLOT_LEFT];
1468 }
1469 else
1470 {
1471 // No weapons to pick, should it pick itself???
1472 loc_target = ( CHR_REF )MAX_CHR;
1473 }
1474 }
1475
1476 // make sure the loc_target is alive
1477 if ( !DEFINED_PCHR( ptarget ) || !ptarget->alive )
1478 {
1479 log_warning( "spawn_one_enchant() - failed because the target is not alive.\n" );
1480 return ( ENC_REF )MAX_ENC;
1481 }
1482 ptarget = ChrList.lst + loc_target;
1483
1484 // Check peve->required_damagetype
1485 if ( peve->required_damagetype < DAMAGE_COUNT )
1486 {
1487 if (
1488 GET_DAMAGE_RESIST( ptarget->damage_modifier[peve->required_damagetype] ) >= 3 ||
1489 HAS_SOME_BITS( ptarget->damage_modifier[peve->required_damagetype], DAMAGECHARGE ) )
1490 {
1491 log_warning( "spawn_one_enchant() - failed because the target is immune to the enchant.\n" );
1492 return ( ENC_REF )MAX_ENC;
1493 }
1494 }
1495
1496 // Check peve->require_damagetarget_damagetype
1497 if ( peve->require_damagetarget_damagetype < DAMAGE_COUNT )
1498 {
1499 if ( ptarget->damagetarget_damagetype != peve->require_damagetarget_damagetype )
1500 {
1501 log_warning( "spawn_one_enchant() - failed because the target not have the right damagetarget_damagetype.\n" );
1502 return ( ENC_REF )MAX_ENC;
1503 }
1504 }
1505
1506 // Find an enchant index to use
1507 enc_ref = EncList_allocate( enc_override );
1508
1509 if ( !ALLOCATED_ENC( enc_ref ) )
1510 {
1511 log_warning( "spawn_one_enchant() - could not allocate an enchant.\n" );
1512 return ( ENC_REF )MAX_ENC;
1513 }
1514 penc = EncList.lst + enc_ref;
1515
1516 POBJ_BEGIN_SPAWN( penc );
1517
1518 penc->spawn_data.owner_ref = owner;
1519 penc->spawn_data.target_ref = loc_target;
1520 penc->spawn_data.spawner_ref = spawner;
1521 penc->spawn_data.profile_ref = loc_profile;
1522 penc->spawn_data.eve_ref = eve_ref;
1523
1524 // actually force the character to spawn
1525 penc = enc_config_activate( penc, 100 );
1526
1527 // log all the successful spawns
1528 if ( NULL != penc )
1529 {
1530 POBJ_END_SPAWN( penc );
1531 peve->create_count++;
1532 }
1533
1534 return enc_ref;
1535 }
1536
1537 //--------------------------------------------------------------------------------------------
load_one_enchant_profile_vfs(const char * szLoadName,const EVE_REF ieve)1538 EVE_REF load_one_enchant_profile_vfs( const char* szLoadName, const EVE_REF ieve )
1539 {
1540 /// @details ZZ@> This function loads an enchantment profile into the EveStack
1541
1542 EVE_REF retval = ( EVE_REF )MAX_EVE;
1543
1544 if ( VALID_EVE_RANGE( ieve ) )
1545 {
1546 eve_t * peve = EveStack.lst + ieve;
1547
1548 if ( NULL != load_one_enchant_file_vfs( szLoadName, peve ) )
1549 {
1550 retval = ieve;
1551
1552 // limit the endsound_index
1553 peve->endsound_index = CLIP( peve->endsound_index, INVALID_SOUND, MAX_WAVE );
1554 }
1555 }
1556
1557 return retval;
1558 }
1559
1560 //--------------------------------------------------------------------------------------------
enchant_remove_set(const ENC_REF ienc,int value_idx)1561 void enchant_remove_set( const ENC_REF ienc, int value_idx )
1562 {
1563 /// @details ZZ@> This function unsets a set value
1564 CHR_REF character;
1565 enc_t * penc;
1566 chr_t * ptarget;
1567
1568 if ( value_idx < 0 || value_idx >= MAX_ENCHANT_SET ) return;
1569
1570 if ( !ALLOCATED_ENC( ienc ) ) return;
1571 penc = EncList.lst + ienc;
1572
1573 if ( value_idx >= MAX_ENCHANT_SET || !penc->setyesno[value_idx] ) return;
1574
1575 if ( !INGAME_CHR( penc->target_ref ) ) return;
1576 character = penc->target_ref;
1577 ptarget = ChrList.lst + penc->target_ref;
1578
1579 switch ( value_idx )
1580 {
1581 case SETDAMAGETYPE:
1582 ptarget->damagetarget_damagetype = penc->setsave[value_idx];
1583 break;
1584
1585 case SETNUMBEROFJUMPS:
1586 ptarget->jumpnumberreset = penc->setsave[value_idx];
1587 break;
1588
1589 case SETLIFEBARCOLOR:
1590 ptarget->lifecolor = penc->setsave[value_idx];
1591 break;
1592
1593 case SETMANABARCOLOR:
1594 ptarget->manacolor = penc->setsave[value_idx];
1595 break;
1596
1597 case SETSLASHMODIFIER:
1598 ptarget->damage_modifier[DAMAGE_SLASH] = penc->setsave[value_idx];
1599 break;
1600
1601 case SETCRUSHMODIFIER:
1602 ptarget->damage_modifier[DAMAGE_CRUSH] = penc->setsave[value_idx];
1603 break;
1604
1605 case SETPOKEMODIFIER:
1606 ptarget->damage_modifier[DAMAGE_POKE] = penc->setsave[value_idx];
1607 break;
1608
1609 case SETHOLYMODIFIER:
1610 ptarget->damage_modifier[DAMAGE_HOLY] = penc->setsave[value_idx];
1611 break;
1612
1613 case SETEVILMODIFIER:
1614 ptarget->damage_modifier[DAMAGE_EVIL] = penc->setsave[value_idx];
1615 break;
1616
1617 case SETFIREMODIFIER:
1618 ptarget->damage_modifier[DAMAGE_FIRE] = penc->setsave[value_idx];
1619 break;
1620
1621 case SETICEMODIFIER:
1622 ptarget->damage_modifier[DAMAGE_ICE] = penc->setsave[value_idx];
1623 break;
1624
1625 case SETZAPMODIFIER:
1626 ptarget->damage_modifier[DAMAGE_ZAP] = penc->setsave[value_idx];
1627 break;
1628
1629 case SETFLASHINGAND:
1630 ptarget->flashand = penc->setsave[value_idx];
1631 break;
1632
1633 case SETLIGHTBLEND:
1634 chr_set_light( ptarget, penc->setsave[value_idx] );
1635 break;
1636
1637 case SETALPHABLEND:
1638 chr_set_alpha( ptarget, penc->setsave[value_idx] );
1639 break;
1640
1641 case SETSHEEN:
1642 chr_set_sheen( ptarget, penc->setsave[value_idx] );
1643 break;
1644
1645 case SETFLYTOHEIGHT:
1646 ptarget->flyheight = penc->setsave[value_idx];
1647 break;
1648
1649 case SETWALKONWATER:
1650 ptarget->waterwalk = ( 0 != penc->setsave[value_idx] );
1651 break;
1652
1653 case SETCANSEEINVISIBLE:
1654 ptarget->see_invisible_level = penc->setsave[value_idx];
1655 break;
1656
1657 case SETMISSILETREATMENT:
1658 ptarget->missiletreatment = penc->setsave[value_idx];
1659 break;
1660
1661 case SETCOSTFOREACHMISSILE:
1662 ptarget->missilecost = penc->setsave[value_idx];
1663 ptarget->missilehandler = character;
1664 break;
1665
1666 case SETMORPH:
1667 // Need special handler for when this is removed
1668 change_character( character, ptarget->basemodel_ref, penc->setsave[value_idx], ENC_LEAVE_ALL );
1669 break;
1670
1671 case SETCHANNEL:
1672 ptarget->canchannel = ( 0 != penc->setsave[value_idx] );
1673 break;
1674 }
1675
1676 penc->setyesno[value_idx] = bfalse;
1677 }
1678
1679 //--------------------------------------------------------------------------------------------
enchant_remove_add(const ENC_REF ienc,int value_idx)1680 void enchant_remove_add( const ENC_REF ienc, int value_idx )
1681 {
1682 /// @details ZZ@> This function undoes cumulative modification to character stats
1683
1684 float fvaluetoadd;
1685 int valuetoadd;
1686 CHR_REF character;
1687 enc_t * penc;
1688 chr_t * ptarget;
1689
1690 if ( value_idx < 0 || value_idx >= MAX_ENCHANT_ADD ) return;
1691
1692 if ( !ALLOCATED_ENC( ienc ) ) return;
1693 penc = EncList.lst + ienc;
1694
1695 if ( !INGAME_CHR( penc->target_ref ) ) return;
1696 character = penc->target_ref;
1697 ptarget = ChrList.lst + penc->target_ref;
1698
1699 if ( penc->addyesno[value_idx] )
1700 {
1701 switch ( value_idx )
1702 {
1703 case ADDJUMPPOWER:
1704 fvaluetoadd = penc->addsave[value_idx];
1705 ptarget->jump_power -= fvaluetoadd;
1706 break;
1707
1708 case ADDBUMPDAMPEN:
1709 fvaluetoadd = penc->addsave[value_idx];
1710 ptarget->phys.bumpdampen -= fvaluetoadd;
1711 break;
1712
1713 case ADDBOUNCINESS:
1714 fvaluetoadd = penc->addsave[value_idx];
1715 ptarget->phys.dampen -= fvaluetoadd;
1716 break;
1717
1718 case ADDDAMAGE:
1719 valuetoadd = penc->addsave[value_idx];
1720 ptarget->damage_boost -= valuetoadd;
1721 break;
1722
1723 case ADDSIZE:
1724 fvaluetoadd = penc->addsave[value_idx];
1725 ptarget->fat_goto -= fvaluetoadd;
1726 ptarget->fat_goto_time = SIZETIME;
1727 break;
1728
1729 case ADDACCEL:
1730 fvaluetoadd = penc->addsave[value_idx];
1731 chr_set_maxaccel( ptarget, ptarget->maxaccel_reset - fvaluetoadd );
1732 break;
1733
1734 case ADDRED:
1735 valuetoadd = penc->addsave[value_idx];
1736 chr_set_redshift( ptarget, ptarget->inst.redshift - valuetoadd );
1737 break;
1738
1739 case ADDGRN:
1740 valuetoadd = penc->addsave[value_idx];
1741 chr_set_grnshift( ptarget, ptarget->inst.grnshift - valuetoadd );
1742 break;
1743
1744 case ADDBLU:
1745 valuetoadd = penc->addsave[value_idx];
1746 chr_set_blushift( ptarget, ptarget->inst.blushift - valuetoadd );
1747 break;
1748
1749 case ADDDEFENSE:
1750 valuetoadd = penc->addsave[value_idx];
1751 ptarget->defense -= valuetoadd;
1752 break;
1753
1754 case ADDMANA:
1755 valuetoadd = penc->addsave[value_idx];
1756 ptarget->manamax -= valuetoadd;
1757 ptarget->mana -= valuetoadd;
1758 if ( ptarget->mana < 0 ) ptarget->mana = 0;
1759 break;
1760
1761 case ADDLIFE:
1762 valuetoadd = penc->addsave[value_idx];
1763 ptarget->lifemax -= valuetoadd;
1764 ptarget->life -= valuetoadd;
1765 if ( ptarget->life < 1 ) ptarget->life = 1;
1766 break;
1767
1768 case ADDSTRENGTH:
1769 valuetoadd = penc->addsave[value_idx];
1770 ptarget->strength -= valuetoadd;
1771 break;
1772
1773 case ADDWISDOM:
1774 valuetoadd = penc->addsave[value_idx];
1775 ptarget->wisdom -= valuetoadd;
1776 break;
1777
1778 case ADDINTELLIGENCE:
1779 valuetoadd = penc->addsave[value_idx];
1780 ptarget->intelligence -= valuetoadd;
1781 break;
1782
1783 case ADDDEXTERITY:
1784 valuetoadd = penc->addsave[value_idx];
1785 ptarget->dexterity -= valuetoadd;
1786 break;
1787 }
1788
1789 penc->addyesno[value_idx] = bfalse;
1790 }
1791 }
1792
1793 //--------------------------------------------------------------------------------------------
1794 //--------------------------------------------------------------------------------------------
init_all_eve()1795 void init_all_eve()
1796 {
1797 EVE_REF cnt;
1798
1799 for ( cnt = 0; cnt < MAX_EVE; cnt++ )
1800 {
1801 eve_init( EveStack.lst + cnt );
1802 }
1803 }
1804
1805 //--------------------------------------------------------------------------------------------
release_all_eve()1806 void release_all_eve()
1807 {
1808 EVE_REF cnt;
1809
1810 for ( cnt = 0; cnt < MAX_EVE; cnt++ )
1811 {
1812 release_one_eve( cnt );
1813 }
1814 }
1815
1816 //--------------------------------------------------------------------------------------------
release_one_eve(const EVE_REF ieve)1817 bool_t release_one_eve( const EVE_REF ieve )
1818 {
1819 eve_t * peve;
1820
1821 if ( !VALID_EVE_RANGE( ieve ) ) return bfalse;
1822 peve = EveStack.lst + ieve;
1823
1824 if ( !peve->loaded ) return btrue;
1825
1826 eve_init( peve );
1827
1828 return btrue;
1829 }
1830
1831 //--------------------------------------------------------------------------------------------
update_all_enchants()1832 void update_all_enchants()
1833 {
1834 ENC_REF ienc;
1835
1836 // update all enchants
1837 for ( ienc = 0; ienc < MAX_ENC; ienc++ )
1838 {
1839 enc_run_config( EncList.lst + ienc );
1840 }
1841
1842 // fix the stat timer
1843 if ( clock_enc_stat >= ONESECOND )
1844 {
1845 // Reset the clock
1846 clock_enc_stat -= ONESECOND;
1847 }
1848 }
1849
1850 //--------------------------------------------------------------------------------------------
cleanup_enchant_list(const ENC_REF ienc,ENC_REF * enc_parent)1851 ENC_REF cleanup_enchant_list( const ENC_REF ienc, ENC_REF * enc_parent )
1852 {
1853 /// @details BB@> remove all the dead enchants from the enchant list
1854 /// and report back the first non-dead enchant in the list.
1855
1856 bool_t enc_used[MAX_ENC];
1857
1858 ENC_REF first_valid_enchant;
1859
1860 ENC_REF ienc_now, ienc_nxt;
1861 size_t ienc_count;
1862
1863 if ( !VALID_ENC_RANGE( ienc ) ) return MAX_ENC;
1864
1865 // clear the list
1866 memset( enc_used, 0, sizeof( enc_used ) );
1867
1868 // scan the list of enchants
1869 ienc_nxt = ( ENC_REF ) MAX_ENC;
1870 first_valid_enchant = ienc_now = ienc;
1871 ienc_count = 0;
1872 while (( MAX_ENC != ienc_now ) && ( ienc_count < MAX_ENC ) )
1873 {
1874 ienc_nxt = EncList.lst[ienc_now].nextenchant_ref;
1875
1876 // coerce the list of enchants to a valid value
1877 if ( !VALID_ENC_RANGE( ienc_nxt ) )
1878 {
1879 ienc_nxt = EncList.lst[ienc_now].nextenchant_ref = MAX_ENC;
1880 }
1881
1882 // fix any loops in the enchant list
1883 if ( enc_used[ienc_nxt] )
1884 {
1885 EncList.lst[ienc_now].nextenchant_ref = MAX_ENC;
1886 break;
1887 }
1888
1889 //( !INGAME_CHR( EncList.lst[ienc_now].target_ref ) && !EveStack.lst[EncList.lst[ienc_now].eve_ref].stayiftargetdead )
1890
1891 // remove any expired enchants
1892 if ( !INGAME_ENC( ienc_now ) )
1893 {
1894 remove_enchant( ienc_now, enc_parent );
1895 enc_used[ienc_now] = btrue;
1896 }
1897 else
1898 {
1899 // store this enchant in the list of used enchants
1900 enc_used[ienc_now] = btrue;
1901
1902 // keep track of the first valid enchant
1903 if ( MAX_ENC == first_valid_enchant )
1904 {
1905 first_valid_enchant = ienc_now;
1906 }
1907 }
1908
1909 enc_parent = &( EncList.lst[ienc_now].nextenchant_ref );
1910 ienc_now = ienc_nxt;
1911 ienc_count++;
1912 }
1913 if ( ienc_count >= MAX_ENC ) log_error( "%s - bad enchant loop\n", __FUNCTION__ );
1914
1915 return first_valid_enchant;
1916 }
1917
1918 //--------------------------------------------------------------------------------------------
cleanup_all_enchants()1919 void cleanup_all_enchants()
1920 {
1921 /// @details ZZ@> this function scans all the enchants and removes any dead ones.
1922 /// this happens only once a loop
1923
1924 ENC_BEGIN_LOOP_ACTIVE( ienc, penc )
1925 {
1926 ENC_REF * enc_lst;
1927 eve_t * peve;
1928 bool_t do_remove;
1929 bool_t valid_owner, valid_target;
1930
1931 // try to determine something about the parent
1932 enc_lst = NULL;
1933 valid_target = bfalse;
1934 if ( INGAME_CHR( penc->target_ref ) )
1935 {
1936 valid_target = ChrList.lst[penc->target_ref].alive;
1937
1938 // this is linked to a known character
1939 enc_lst = &( ChrList.lst[penc->target_ref].firstenchant );
1940 }
1941
1942 //try to determine if the owner exists and is alive
1943 valid_owner = bfalse;
1944 if ( INGAME_CHR( penc->owner_ref ) )
1945 {
1946 valid_owner = ChrList.lst[penc->owner_ref].alive;
1947 }
1948
1949 if ( !LOADED_EVE( penc->eve_ref ) )
1950 {
1951 // this should never happen
1952 EGOBOO_ASSERT( bfalse );
1953 continue;
1954 }
1955 peve = EveStack.lst + penc->eve_ref;
1956
1957 do_remove = bfalse;
1958 if ( WAITING_PBASE( POBJ_GET_PBASE( penc ) ) )
1959 {
1960 // the enchant has been marked for removal
1961 do_remove = btrue;
1962 }
1963 else if ( !valid_owner && !peve->stayifnoowner )
1964 {
1965 // the enchant's owner has died
1966 do_remove = btrue;
1967 }
1968 else if ( !valid_target && !peve->stayiftargetdead )
1969 {
1970 // the enchant's target has died
1971 do_remove = btrue;
1972 }
1973 else if ( valid_owner && peve->endifcantpay )
1974 {
1975 // Undo enchants that cannot be sustained anymore
1976 if ( 0 == ChrList.lst[penc->owner_ref].mana ) do_remove = btrue;
1977 }
1978 else
1979 {
1980 // the enchant has timed out
1981 do_remove = ( 0 == penc->lifetime );
1982 }
1983
1984 if ( do_remove )
1985 {
1986 remove_enchant( ienc, NULL );
1987 }
1988 }
1989 ENC_END_LOOP();
1990 }
1991
1992 //--------------------------------------------------------------------------------------------
bump_all_enchants_update_counters()1993 void bump_all_enchants_update_counters()
1994 {
1995 ENC_REF cnt;
1996
1997 for ( cnt = 0; cnt < MAX_ENC; cnt++ )
1998 {
1999 obj_data_t * pbase;
2000
2001 pbase = POBJ_GET_PBASE( EncList.lst + cnt );
2002 if ( !ACTIVE_PBASE( pbase ) ) continue;
2003
2004 pbase->update_count++;
2005 }
2006 }
2007
2008 //--------------------------------------------------------------------------------------------
enc_request_terminate(const ENC_REF ienc)2009 bool_t enc_request_terminate( const ENC_REF ienc )
2010 {
2011 if ( !ALLOCATED_ENC( ienc ) || TERMINATED_ENC( ienc ) ) return bfalse;
2012
2013 POBJ_REQUEST_TERMINATE( EncList.lst + ienc );
2014
2015 return btrue;
2016 }
2017