1 /*
2  * OpenBOR - http://www.chronocrash.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) OpenBOR Team
7  */
8 
9 /////////////////////////////////////////////////////////////////////////////
10 //	Beats of Rage                                                           //
11 //	Side-scrolling beat-'em-up                                              //
12 /////////////////////////////////////////////////////////////////////////////
13 
14 #include "openbor.h"
15 #include "commands.h"
16 #include "models.h"
17 #include "translation.h"
18 #include "soundmix.h"
19 
20 #define NaN 0xAAAAAAAA
21 
22 static const char *E_OUT_OF_MEMORY = "Error: Could not allocate sufficient memory.\n";
23 static int DEFAULT_OFFSCREEN_KILL = 3000;
24 
25 
26 s_sprite_list *sprite_list;
27 s_sprite_map *sprite_map;
28 
29 s_savelevel *savelevel;
30 s_savescore savescore;
31 s_savedata savedata;
32 
33 /////////////////////////////////////////////////////////////////////////////
34 //  Global Variables                                                        //
35 /////////////////////////////////////////////////////////////////////////////
36 
37 a_playrecstatus *playrecstatus = NULL;
38 s_anim_list *anim_list = NULL;
39 s_modelcache *model_cache = NULL;
40 
41 s_set_entry *levelsets = NULL;
42 int        num_difficulties;
43 
44 int no_cmd_compatible = 0;
45 
46 int		skiptoset = -1;
47 //when there are more entities than this, those with lower priority will be erased
48 int spawnoverride = 999999;
49 int maxentities = 999999;
50 
51 int	global_model = -1;
52 #define global_model_scripts ((global_model>=0 && model_cache[global_model].model)?model_cache[global_model].model->scripts:NULL)
53 
54 s_level            *level               = NULL;
55 s_filestream *filestreams = NULL;
56 int numfilestreams = 0;
57 s_screen           *vscreen             = NULL;
58 s_screen           *background          = NULL;
59 s_videomodes        videomodes;
60 int sprite_map_max_items = 0;
61 int cache_map_max_items = 0;
62 
63 int startup_done = 0; // startup is only called when a game is loaded. so when exitting from the menu we need a way to figure out which resources to free.
64 List *modelcmdlist = NULL;
65 List *modelstxtcmdlist = NULL;
66 List *levelcmdlist = NULL;
67 List *levelordercmdlist = NULL;
68 
69 int atkchoices[MAX_ANIS]; //tempory values for ai functions, should be well enough LOL
70 
71 //see types.h
72 const s_drawmethod plainmethod =
73 {
74     .table      = NULL,
75     .fillcolor  = 0,
76     .flag       = 1,
77     .alpha      = BLEND_MODE_MODEL,
78     .remap      = -1,
79     .flipx      = 0,
80     .flipy      = 0,
81     .transbg    = 0,
82     .fliprotate = 0,
83     .rotate     = 0,
84     .scalex     = 256,
85     .scaley     = 256,
86     .shiftx     = 0,
87     .centerx    = 0,
88     .centery    = 0,
89     .xrepeat    = 1,
90     .yrepeat    = 1,
91     .xspan      = 0,
92     .yspan      = 0,
93     .channelr   = 255,
94     .channelg   = 255,
95     .channelb   = 255,
96     .tintmode   = 0,
97     .tintcolor  = 0,
98     .clipx      = 0,
99     .clipy      = 0,
100     .clipw      = 0,
101     .cliph      = 0,
102     .water = {{.beginsize = 0.0}, {.endsize = 0.0}, 0, {.wavespeed = 0}, 0}
103 };
104 
105 const s_defense default_defense =
106 {
107     .factor         = 1.f,
108     .pain           = 0.f,
109     .knockdown      = 1.f,
110     .blockpower     = 0.f,
111     .blockthreshold = 0.f,
112     .blockratio     = 0.f,
113     .blocktype      = BLOCK_TYPE_MP_FIRST
114 };
115 
116 const s_hitbox empty_collision_coords = {   .x      = 0,
117                                             .y      = 0,
118                                             .width  = 0,
119                                             .height = 0,
120                                             .z1     = 0,
121                                             .z2     = 0};
122 
123 const s_collision_body empty_body =   {     .coords     = NULL,
124                                             .index      = 0,
125                                             .defense    = NULL,
126                                             .tag        = 0};
127 
128 const s_collision_entity empty_entity_collision =   {   .coords     = NULL,
129                                                         .index      = 0,
130                                                         .tag        = 0};
131 
132 // Recursive damage (dot).
133 const s_damage_recursive empty_recursive = {    .force  = 0,
134                                                 .index  = 0,
135                                                 .mode   = 0,
136                                                 .rate   = 0,
137                                                 .tick	= 0,
138 												.time   = 0,
139 												.owner	= NULL,
140 												.next	= NULL};
141 
142 // unknockdown attack
143 const s_collision_attack emptyattack =
144 {
145     .attack_drop        = 0,
146     .attack_force       = 0,
147     .attack_type        = ATK_NORMAL,
148     .blast              = 0,
149     .blockflash         = -1,
150     .blocksound         = -1,
151     .coords             = NULL,
152     .counterattack      = 0,
153     .damage_on_landing.attack_force =  0,
154     .damage_on_landing.attack_type = ATK_NONE,
155     .dropv              = { .x = 0,
156                             .y = 0,
157                             .z = 0},
158     .force_direction    = DIRECTION_ADJUST_NONE,
159     .forcemap           = 0,
160     .freeze             = 0,
161     .freezetime         = 0,
162     .grab               = 0,
163     .grab_distance      = 0,
164     .guardcost          = 0,
165     .hitflash           = -1,
166     .hitsound           = -1,
167     .jugglecost         = 0,
168     .maptime            = 0,
169     .no_block           = 0,
170     .no_flash           = 0,
171     .no_kill            = 0,
172     .no_pain            = 0,
173     .otg                = OTG_NONE,
174     .next_hit_time          = 0,
175     .pause_add          = 0,
176     .recursive          = NULL,
177     .seal               = 0,
178     .sealtime           = 0,
179     .staydown           = { .rise               = 0,
180                             .riseattack         = 0,
181                             .riseattack_stall   = 0},
182     .steal              = 0,
183     .tag                = 0
184 };
185 
186 s_axis_principal_float default_model_dropv =
187 {
188     /* Default values for knockdown velocity */
189 
190     .x = 1.2f,
191     .y = 3.f,
192     .z = 0.f
193 };
194 
195 //default values
196 float default_level_maxtossspeed = 100.0f;
197 float default_level_maxfallspeed = -6.0f;
198 float default_level_gravity = -0.1f;
199 
200 float default_model_jumpheight = 4.0f;
201 float default_model_jumpspeed = -1;
202 float default_model_grabdistance = 36.0f;
203 
204 // AI attack debug stuff for development purpose,
205 // Don't open them to modders yet
206 float move_noatk_factor = 3.0f;
207 float group_noatk_factor = 0.01f;
208 float agg_noatk_factor = 0.0f;
209 float min_noatk_chance = 0.0f;
210 float max_noatk_chance = 0.6f;
211 float offscreen_noatk_factor = 0.5f;
212 float noatk_duration = 0.75f;
213 
214 char                *custScenes = NULL;
215 char                *custBkgrds = NULL;
216 char                *custLevels = NULL;
217 char                *custModels = NULL;
218 char                rush_names[2][MAX_NAME_LEN];
219 char				skipselect[MAX_PLAYERS][MAX_NAME_LEN];
220 char                branch_name[MAX_NAME_LEN + 1];  // Used for branches
221 char                allowselect_args[MAX_ALLOWSELECT_LEN]; // stored allowselect players
222 int					useSave = 0;
223 int					useSet = -1;
224 unsigned char       pal[MAX_PAL_SIZE] = {""};
225 int                 blendfx[MAX_BLENDINGS] = {0, 1, 0, 0, 0, 0};
226 char                blendfx_is_set = 0;
227 int                 fontmonospace[MAX_FONTS] = {0, 0, 0, 0, 0, 0, 0, 0};
228 int                 fontmbs[MAX_FONTS] = {0, 0, 0, 0, 0, 0, 0, 0};
229 
230 // function pointers to create the blending tables
231 blend_table_function blending_table_functions32[MAX_BLENDINGS] = {create_screen32_tbl, create_multiply32_tbl, create_overlay32_tbl, create_hardlight32_tbl, create_dodge32_tbl, create_half32_tbl};
232 
233 int                 current_set = 0;
234 int                 current_level = 0;
235 int                 current_stage = 1;
236 
237 int					timevar;
238 float               bgtravelled;
239 float               vbgtravelled;
240 int                 traveltime;
241 int                 texttime;
242 int					timetoshow;
243 int                 is_total_timeover = 0;
244 int					showgo;
245 float               advancex;
246 float               advancey;
247 
248 float               scrolldx;                       // advancex changed previous loop
249 float               scrolldy;                       // advancey .....................
250 float               scrollminz;                     // Limit level z-scroll
251 float               scrollmaxz;
252 float               blockade;                    // Limit x scroll back
253 float				scrollminx;
254 float				scrollmaxx;
255 
256 s_lasthit           lasthit;  //Last collision variables. 2013-12-15, moved to struct.
257 
258 int					combodelay = GAME_SPEED / 2;		// avoid annoying 112112... infinite combo
259 
260 //Use for gfx_shadow
261 s_axis_plane_vertical_int light = {   .x = 128,
262                         .y = 64};
263 
264 int                 shadowcolor = 0;
265 int                 shadowalpha = BLEND_MULTIPLY + 1;
266 int                 shadowopacity = 255;
267 
268 u64 totalram = 0;
269 u64 usedram = 0;
270 u64 freeram = 0;
271 u32 interval = 0;
272 //extern u64 seed;
273 
274 int                 SAMPLE_GO			= -1;
275 int                 SAMPLE_BEAT			= -1;
276 int                 SAMPLE_BLOCK		= -1;
277 int                 SAMPLE_INDIRECT		= -1;
278 int                 SAMPLE_GET			= -1;
279 int                 SAMPLE_GET2			= -1;
280 int                 SAMPLE_FALL			= -1;
281 int                 SAMPLE_JUMP			= -1;
282 int                 SAMPLE_PUNCH		= -1;
283 int                 SAMPLE_1UP			= -1;
284 int                 SAMPLE_TIMEOVER		= -1;
285 int                 SAMPLE_BEEP			= -1;
286 int                 SAMPLE_BEEP2		= -1;
287 int                 SAMPLE_BIKE			= -1;
288 int                 SAMPLE_PAUSE		= -1;
289 
290 // 2016-11-01
291 // Caskey, Damon V.
292 //
293 // Collision indexes. Only using globals while
294 // building multiple collision box support.
295 // Once we get this working, variables should
296 // be moved into a structure. Globals BAD!
297 int                 max_collisons       = MAX_COLLISIONS;
298 int                 *collisions         = NULL;
299 
300 
301 int                 max_downs           = MAX_DOWNS;
302 int                 max_ups             = MAX_UPS;
303 int                 max_backwalks       = MAX_BACKWALKS;
304 int                 max_walks           = MAX_WALKS;
305 int                 max_idles           = MAX_IDLES;
306 int                 max_attack_types    = MAX_ATKS;
307 int                 max_freespecials    = MAX_SPECIALS;
308 int                 max_follows         = MAX_FOLLOWS;
309 int                 max_attacks         = MAX_ATTACKS;
310 int                 max_animations      = MAX_ANIS;
311 
312 // -------dynamic animation indexes-------
313 e_animations	*animdowns           = NULL;
314 e_animations    *animups             = NULL;
315 e_animations    *animbackwalks       = NULL;
316 e_animations	*animwalks           = NULL;
317 e_animations    *animidles           = NULL;
318 e_animations    *animpains           = NULL;
319 e_animations    *animbackpains       = NULL;
320 e_animations    *animdies            = NULL;
321 e_animations    *animbackdies        = NULL;
322 e_animations    *animfalls           = NULL;
323 e_animations    *animbackfalls       = NULL;
324 e_animations    *animrises           = NULL;
325 e_animations    *animbackrises       = NULL;
326 e_animations    *animriseattacks     = NULL;
327 e_animations    *animbackriseattacks = NULL;
328 e_animations    *animblkpains        = NULL;
329 e_animations    *animbackblkpains    = NULL;
330 e_animations    *animattacks         = NULL;
331 e_animations    *animfollows         = NULL;
332 e_animations    *animspecials        = NULL;
333 
334 // system default values
335 int                 downs[MAX_DOWNS]        = {ANI_DOWN};
336 int                 ups[MAX_UPS]            = {ANI_UP};
337 int                 backwalks[MAX_BACKWALKS] = {ANI_BACKWALK};
338 int                 walks[MAX_WALKS]        = {ANI_WALK};
339 int                 idles[MAX_IDLES]        = {ANI_IDLE};
340 
341 int                 falls[MAX_ATKS] =
342 {
343     ANI_FALL,  ANI_FALL2, ANI_FALL3,  ANI_FALL4,
344     ANI_FALL,  ANI_BURN,  ANI_FALL,   ANI_SHOCK,
345     ANI_FALL,  ANI_FALL5, ANI_FALL6,  ANI_FALL7,
346     ANI_FALL8, ANI_FALL9, ANI_FALL10, ANI_FALL,
347     ANI_FALL,  ANI_FALL,  ANI_FALL,   ANI_FALLLOSE,
348     ANI_FALL
349 };
350 
351 int                 backfalls[MAX_ATKS] =
352 {
353     ANI_BACKFALL,  ANI_BACKFALL2, ANI_BACKFALL3,  ANI_BACKFALL4,
354     ANI_BACKFALL,  ANI_BACKBURN,  ANI_BACKFALL,   ANI_BACKSHOCK,
355     ANI_BACKFALL,  ANI_BACKFALL5, ANI_BACKFALL6,  ANI_BACKFALL7,
356     ANI_BACKFALL8, ANI_BACKFALL9, ANI_BACKFALL10, ANI_BACKFALL,
357     ANI_BACKFALL,  ANI_BACKFALL,  ANI_BACKFALL,   ANI_FALLLOSE,
358     ANI_BACKFALL
359 };
360 
361 int                 rises[MAX_ATKS] =
362 {
363     ANI_RISE,  ANI_RISE2, ANI_RISE3,  ANI_RISE4,
364     ANI_RISE,  ANI_RISEB,  ANI_RISE,  ANI_RISES,
365     ANI_RISE,  ANI_RISE5, ANI_RISE6,  ANI_RISE7,
366     ANI_RISE8, ANI_RISE9, ANI_RISE10, ANI_RISE,
367     ANI_RISE,  ANI_RISE,  ANI_RISE,   ANI_RISE,
368     ANI_RISE
369 };
370 
371 int                 backrises[MAX_ATKS] =
372 {
373     ANI_BACKRISE,  ANI_BACKRISE2, ANI_BACKRISE3,  ANI_BACKRISE4,
374     ANI_BACKRISE,  ANI_BACKRISEB, ANI_BACKRISE,   ANI_BACKRISES,
375     ANI_BACKRISE,  ANI_BACKRISE5, ANI_BACKRISE6,  ANI_BACKRISE7,
376     ANI_BACKRISE8, ANI_BACKRISE9, ANI_BACKRISE10, ANI_BACKRISE,
377     ANI_BACKRISE,  ANI_BACKRISE,  ANI_BACKRISE,   ANI_BACKRISE,
378     ANI_BACKRISE
379 };
380 
381 int                 riseattacks[MAX_ATKS] =
382 {
383     ANI_RISEATTACK,  ANI_RISEATTACK2, ANI_RISEATTACK3,  ANI_RISEATTACK4,
384     ANI_RISEATTACK,  ANI_RISEATTACKB, ANI_RISEATTACK,   ANI_RISEATTACKS,
385     ANI_RISEATTACK,  ANI_RISEATTACK5, ANI_RISEATTACK6,  ANI_RISEATTACK7,
386     ANI_RISEATTACK8, ANI_RISEATTACK9, ANI_RISEATTACK10, ANI_RISEATTACK,
387     ANI_RISEATTACK,  ANI_RISEATTACK,  ANI_RISEATTACK,   ANI_RISEATTACK,
388     ANI_RISEATTACK
389 };
390 
391 int                 backriseattacks[MAX_ATKS] =
392 {
393     ANI_BACKRISEATTACK,  ANI_BACKRISEATTACK2, ANI_BACKRISEATTACK3,  ANI_BACKRISEATTACK4,
394     ANI_BACKRISEATTACK,  ANI_BACKRISEATTACKB, ANI_BACKRISEATTACK,   ANI_BACKRISEATTACKS,
395     ANI_BACKRISEATTACK,  ANI_BACKRISEATTACK5, ANI_BACKRISEATTACK6,  ANI_BACKRISEATTACK7,
396     ANI_BACKRISEATTACK8, ANI_BACKRISEATTACK9, ANI_BACKRISEATTACK10, ANI_BACKRISEATTACK,
397     ANI_BACKRISEATTACK,  ANI_BACKRISEATTACK,  ANI_BACKRISEATTACK,   ANI_BACKRISEATTACK,
398     ANI_BACKRISEATTACK
399 };
400 
401 int                 pains[MAX_ATKS] =
402 {
403     ANI_PAIN,  ANI_PAIN2,    ANI_PAIN3,  ANI_PAIN4,
404     ANI_PAIN,  ANI_BURNPAIN, ANI_PAIN,   ANI_SHOCKPAIN,
405     ANI_PAIN,  ANI_PAIN5,    ANI_PAIN6,  ANI_PAIN7,
406     ANI_PAIN8, ANI_PAIN9,    ANI_PAIN10, ANI_PAIN,
407     ANI_PAIN,  ANI_PAIN,     ANI_PAIN,   ANI_PAIN,
408     ANI_PAIN
409 };
410 
411 int                 backpains[MAX_ATKS] =
412 {
413     ANI_BACKPAIN,  ANI_BACKPAIN2,    ANI_BACKPAIN3,  ANI_BACKPAIN4,
414     ANI_BACKPAIN,  ANI_BACKBURNPAIN, ANI_BACKPAIN,   ANI_BACKSHOCKPAIN,
415     ANI_BACKPAIN,  ANI_BACKPAIN5,    ANI_BACKPAIN6,  ANI_BACKPAIN7,
416     ANI_BACKPAIN8, ANI_BACKPAIN9,    ANI_BACKPAIN10, ANI_BACKPAIN,
417     ANI_BACKPAIN,  ANI_BACKPAIN,     ANI_BACKPAIN,   ANI_BACKPAIN,
418     ANI_BACKPAIN
419 };
420 
421 int                 deaths[MAX_ATKS] =
422 {
423     ANI_DIE,   ANI_DIE2,     ANI_DIE3,  ANI_DIE4,
424     ANI_DIE,   ANI_BURNDIE,  ANI_DIE,   ANI_SHOCKDIE,
425     ANI_DIE,   ANI_DIE5,     ANI_DIE6,  ANI_DIE7,
426     ANI_DIE8,  ANI_DIE9,     ANI_DIE10, ANI_DIE,
427     ANI_DIE,   ANI_DIE,      ANI_DIE,   ANI_LOSE,
428     ANI_DIE
429 };
430 
431 int                 backdeaths[MAX_ATKS] =
432 {
433     ANI_BACKDIE,   ANI_BACKDIE2,     ANI_BACKDIE3,  ANI_BACKDIE4,
434     ANI_BACKDIE,   ANI_BACKBURNDIE,  ANI_BACKDIE,   ANI_BACKSHOCKDIE,
435     ANI_BACKDIE,   ANI_BACKDIE5,     ANI_BACKDIE6,  ANI_BACKDIE7,
436     ANI_BACKDIE8,  ANI_BACKDIE9,     ANI_BACKDIE10, ANI_BACKDIE,
437     ANI_BACKDIE,   ANI_BACKDIE,      ANI_BACKDIE,   ANI_LOSE,
438     ANI_BACKDIE
439 };
440 
441 int                 blkpains[MAX_ATKS] =
442 {
443     ANI_BLOCKPAIN,  ANI_BLOCKPAIN2, ANI_BLOCKPAIN3,  ANI_BLOCKPAIN4,
444     ANI_BLOCKPAIN,  ANI_BLOCKPAINB, ANI_BLOCKPAIN,   ANI_BLOCKPAINS,
445     ANI_BLOCKPAIN,  ANI_BLOCKPAIN5, ANI_BLOCKPAIN6,  ANI_BLOCKPAIN7,
446     ANI_BLOCKPAIN8, ANI_BLOCKPAIN9, ANI_BLOCKPAIN10, ANI_BLOCKPAIN,
447     ANI_BLOCKPAIN,  ANI_BLOCKPAIN,  ANI_BLOCKPAIN,   ANI_BLOCKPAIN,
448     ANI_BLOCKPAIN
449 };
450 
451 int                 backblkpains[MAX_ATKS] =
452 {
453     ANI_BACKBLOCKPAIN,  ANI_BACKBLOCKPAIN2, ANI_BACKBLOCKPAIN3,  ANI_BACKBLOCKPAIN4,
454     ANI_BACKBLOCKPAIN,  ANI_BACKBLOCKPAINB, ANI_BACKBLOCKPAIN,   ANI_BACKBLOCKPAINS,
455     ANI_BACKBLOCKPAIN,  ANI_BACKBLOCKPAIN5, ANI_BACKBLOCKPAIN6,  ANI_BACKBLOCKPAIN7,
456     ANI_BACKBLOCKPAIN8, ANI_BACKBLOCKPAIN9, ANI_BACKBLOCKPAIN10, ANI_BACKBLOCKPAIN,
457     ANI_BACKBLOCKPAIN,  ANI_BACKBLOCKPAIN,  ANI_BACKBLOCKPAIN,   ANI_BACKBLOCKPAIN,
458     ANI_BACKBLOCKPAIN
459 };
460 
461 int                 normal_attacks[MAX_ATTACKS] =
462 {
463     ANI_ATTACK1, ANI_ATTACK2, ANI_ATTACK3, ANI_ATTACK4
464 };
465 
466 int                 grab_attacks[GRAB_ACTION_SELECT_MAX][2] =
467 {
468     [GRAB_ACTION_SELECT_ATTACK] = {ANI_GRABATTACK, ANI_GRABATTACK2},
469 	[GRAB_ACTION_SELECT_BACKWARD] = {ANI_GRABBACKWARD, ANI_GRABBACKWARD2},
470 	[GRAB_ACTION_SELECT_FORWARD] = {ANI_GRABFORWARD, ANI_GRABFORWARD2},
471     [GRAB_ACTION_SELECT_DOWN] = {ANI_GRABDOWN, ANI_GRABDOWN2},
472 	[GRAB_ACTION_SELECT_UP] = {ANI_GRABUP, ANI_GRABUP2}
473 };
474 
475 int                 freespecials[MAX_SPECIALS] =
476 {
477     ANI_FREESPECIAL,   ANI_FREESPECIAL2,  ANI_FREESPECIAL3,
478     ANI_FREESPECIAL4,  ANI_FREESPECIAL5,  ANI_FREESPECIAL6,
479     ANI_FREESPECIAL7,  ANI_FREESPECIAL8
480 };
481 
482 int                 follows[MAX_FOLLOWS] =
483 {
484     ANI_FOLLOW1, ANI_FOLLOW2, ANI_FOLLOW3, ANI_FOLLOW4
485 };
486 
487 // background cache to speed up in-game menus
488 #ifdef CACHE_BACKGROUNDS
489 s_screen           *bg_cache[MAX_CACHED_BACKGROUNDS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
490 unsigned char		bg_palette_cache[MAX_CACHED_BACKGROUNDS][MAX_PAL_SIZE];
491 #endif
492 
493 s_debug_xy_msg      debug_xy_msg;
494 int                 cameratype          = 0;
495 int					defaultmaxplayers	= 2;
496 
497 u32                 go_time             = 0;
498 u32                 _time               = 0;
499 u32                 newtime             = 0;
500 s_slow_motion       slowmotion          = { .toggle     = SLOW_MOTION_OFF,
501                                             .counter    = 0,
502                                             .duration   = 2};
503 int                 disablelog          = 0;
504 int                 currentspawnplayer  = 0;
505 int					ent_list_size		= 0;
506 int                 PLAYER_MIN_Z        = 160;
507 int                 PLAYER_MAX_Z        = 232;
508 int                 BGHEIGHT            = 160;
509 int                 MAX_WALL_HEIGHT     = 1000;					// Max wall height that an entity can be spawned on
510 int                 saveslot            = 0;
511 int                 current_palette     = 0;
512 int                 fade                = 24;
513 int                 credits             = 0;
514 int                 gosound             = 0;					// Used to prevent go sound playing too frequently,
515 int                 musicoverlap        = 0;
516 int                 colorbars           = 0;
517 int                 current_spawn       = 0;
518 int                 level_completed     = 0;
519 int                 level_completed_defeating_boss     = 0;
520 int                 nojoin              = 0;					// dont allow new hero to join in, use "Please Wait" instead of "Select Hero"
521 int                 groupmin            = 0;
522 int					groupmax            = 0;
523 int                 selectScreen        = 0;					// Flag to determine if at select screen (used for setting animations)
524 int					titleScreen			= 0;
525 int					menuScreen			= 0;
526 int					enginecreditsScreen		= 0;								// CRxTRDude - Flag to determine if the credits for the engine is shown.
527 int					hallOfFame			= 0;
528 int					optionsMenu			= 0;
529 int					newgameMenu			= 0;
530 int					loadgameMenu		= 0;
531 int					controloptionsMenu	= 0;
532 int					videooptionsMenu	= 0;
533 int					soundoptionsMenu	= 0;
534 int					systemoptionsMenu	= 0;
535 int					startgameMenu		= 0;
536 int					gameOver			= 0;
537 int					showComplete		= 0;
538 char				*currentScene		= NULL;
539 int                 tospeedup           = 0;          			// If set will speed the level back up after a boss hits the ground
540 int                 reached[MAX_PLAYERS]          = {0, 0, 0, 0};			// Used with TYPE_ENDLEVEL to determine which players have reached the point //4player
541 int                 noslowfx			= 0;           			// Flag to determine if sound speed when hitting opponent slows or not
542 int                 equalairpause 		= 0;         			// If set to 1, there will be no extra pausetime for players who hit multiple enemies in midair
543 int                 hiscorebg			= 0;					// If set to 1, will look for a background image to display at the highscore screen
544 int                 completebg			= 0;           			// If set to 1, will look for a background image to display at the showcomplete screen
545 s_loadingbar        loadingbg[2] = {{0, 0, {0, 0}, {0, 0}, 0, 0}, {0, 0, {0, 0}, {0, 0}, 0, 0}}; // If set to 1, will look for a background image to display at the loading screen
546 int					loadingmusic        = 0;
547 int                 unlockbg            = 0;         			// If set to 1, will look for a different background image after defeating the game
548 int                 _pause              = 0;
549 int                 goto_mainmenu_flag  = 0;
550 int                 escape_flag         = 0;                    // Kratus (20-04-21) Added the new "escape" flag in the select screen, has the same effect as the esc key but now accessible by the "gotomainmenu" function
551 int					nofadeout			= 0;
552 int					nosave				= 0;
553 int                 nopause             = 0;                    // OX. If set to 1 , pausing the game will be disabled.
554 int                 noscreenshot        = 0;                    // OX. If set to 1 , taking screenshots is disabled.
555 int                 endgame             = 0;
556 int                 forcecheatsoff      = 0;
557 int                 nodebugoptions      = 0;
558 int                 cheats              = 0;
559 int                 livescheat          = 0;
560 int                 keyscriptrate       = 0;
561 int                 creditscheat        = 0;
562 int                 healthcheat         = 0;
563 int                 multihitcheat       = 0;					//Kratus (20-04-21) Flag to enable or disable the multihit glitch option
564 int                 showtimeover        = 0;
565 int                 sameplayer          = 0;            		// 7-1-2005  flag to determine if players can use the same character
566 int                 PLAYER_LIVES        = 3;					// 7-1-2005  default setting for Lives
567 int                 CONTINUES           = 5;					// 7-1-2005  default setting for continues
568 int                 colourselect		= 0;					// 6-2-2005 Colour select is optional
569 int                 autoland			= 0;					// Default set to no autoland and landing is valid with u j combo
570 int                 ajspecial			= 0;					// Flag to determine if holding down attack and pressing jump executes special
571 int                 nolost				= 0;					// variable to control if drop weapon when grab a enemy by tails
572 int                 nocost				= 0;					// If set, special will not cost life unless an enemy is hit
573 int                 mpstrict			= 0;					// If current system will check all animation's energy cost when set new animations
574 int                 magic_type			= 0;					// use for restore mp by time by tails
575 entity             *textbox				= NULL;
576 entity             *smartbomber			= NULL;
577 entity				*stalker				= NULL;					// an enemy (usually) tries to go behind the player
578 entity				*firstplayer			= NULL;
579 int					stalking			= 0;
580 int					nextplan			= 0;
581 int                 plife[MAX_PLAYERS][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player lifebar
582 int                 plifeX[MAX_PLAYERS][3]        = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable player lifebar 'x'
583 int                 plifeN[MAX_PLAYERS][3]        = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable player lifebar number of lives
584 int                 picon[MAX_PLAYERS][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player icon
585 int                 piconw[MAX_PLAYERS][2]        = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player weapon icons
586 int                 mpicon[MAX_PLAYERS][2]        = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable magicbar player icon
587 int                 pnameJ[MAX_PLAYERS][7]        = {{0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, -1}}; // Used for customizable player name, Select Hero, (Credits, Press Start, Game Over) when joining
588 int                 pscore[MAX_PLAYERS][7]        = {{0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, -1}}; // Used for customizable player name, dash, score
589 int                 pshoot[MAX_PLAYERS][3]        = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable player shootnum
590 int                 prush[MAX_PLAYERS][8]         = {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}; // Used for customizable player combo/rush system
591 int                 psmenu[MAX_PLAYERS][4]        = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; // Used for customizable player placement in select menu
592 int                 mpcolourtable[11]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
593 int                 hpcolourtable[11]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
594 int                 ldcolourtable[11]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
595 char                musicname[MAX_BUFFER_LEN]      = {""};
596 char                currentmusic[MAX_BUFFER_LEN]    = {""};
597 float               musicfade[2]        = {0, 0};
598 int                 musicloop           = 0;
599 u32                 musicoffset         = 0;
600 int					alwaysupdate		= 0; //execute update/updated scripts whenever it has a chance
601 
602 s_barstatus loadingbarstatus =
603 {
604     .size           = { .x = 0,
605                         .y = 10},
606     .offset         = { .x = 0,
607                         .y = 0},
608     .type           = PERCENTAGEBAR,
609     .orientation    = HORIZONTALBAR,
610     .noborder       = 0,
611     .direction      = BARSTATUS_DIR_NORMAL,
612     .barlayer       = 0,
613     .backlayer      = 0,
614     .borderlayer    = 0,
615     .shadowlayer    = 0,
616     .colourtable    = &ldcolourtable
617 };
618 
619 s_barstatus lbarstatus, olbarstatus =
620 {
621     .size           = { .x = 0,
622                         .y = 0},
623     .offset         = { .x = 0,
624                         .y = 0},
625     .type           = VALUEBAR,
626     .orientation    = HORIZONTALBAR,
627     .noborder       = 0,
628     .direction      = BARSTATUS_DIR_NORMAL,
629     .barlayer       = 0,
630     .backlayer      = 0,
631     .borderlayer    = 0,
632     .shadowlayer    = 0,
633     .colourtable    = &hpcolourtable
634 };
635 
636 s_barstatus mpbarstatus =
637 {
638     .size           = { .x = 0,
639                         .y = 0},
640     .offset         = { .x = 0,
641                         .y = 0},
642     .type           = VALUEBAR,
643     .orientation    = HORIZONTALBAR,
644     .noborder       = 0,
645     .direction      = BARSTATUS_DIR_NORMAL,
646     .barlayer       = 0,
647     .backlayer      = 0,
648     .borderlayer    = 0,
649     .shadowlayer    = 0,
650     .colourtable    = &mpcolourtable
651 };
652 
653 int                 timeloc[6]			= {0, 0, 0, 0, 0, -1};		// Used for customizable timeclock location/size
654 int                 timeicon			= -1;
655 int                 timeicon_offsets[2] = {0, 0};
656 char                timeicon_path[MAX_BUFFER_LEN]  = {""};
657 int                 bgicon   			= -1;
658 int                 bgicon_offsets[3]	= {0, 0, 0};
659 char                bgicon_path[MAX_BUFFER_LEN]    = {""};
660 int                 olicon    			= -1;
661 int                 olicon_offsets[3]	= {0, 0, 0};
662 char                olicon_path[MAX_BUFFER_LEN]    = {""};
663 int                 elife[4][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable enemy lifebar
664 int                 ename[4][3]         = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable enemy name
665 int                 eicon[4][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable enemy icon
666 int                 scomplete[6]		= {0, 0, 0, 0, 0, 0};		// Used for customizable Stage # Complete
667 int                 cbonus[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable clear bonus
668 int                 lbonus[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable life bonus
669 int                 rbonus[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable rush bonus
670 int                 tscore[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable total score
671 int                 scbonuses[4]        = {10000, 1000, 100, 0};//Stage complete bonus multipliers
672 int                 showrushbonus       = 0;
673 int                 noshare				= 0;					// Used for when you want to keep p1 & p2 credits separate
674 int                 nodropen			= 0;					// Drop or not when spawning is now a modder option
675 int					nodropspawn			= 0;					// don't spawn from the sky if the modder doesn't set it
676 int                 gfx_x_offset		= 0;                    //2011_04_03, DC: Enable X offset adjustment by modders.
677 int                 gfx_y_offset		= 0;
678 int                 gfx_y_offset_adj    = 0;                    //2011_04_03, DC: Enable Y offset adjustment by modders.
679 
680 // 2011/10/22 UT: temporary solution for custom viewport
681 int					viewportx			= 0;
682 int					viewporty			= 0;
683 int					viewportw			= 0;
684 int					viewporth			= 0;
685 
686 
687 int                 timeleft			= 0;
688 int                 oldtime             = 0;                    // One second back from time left.
689 int                 holez				= 0;					// Used for setting spawn points
690 int                 allow_secret_chars	= 0;
691 unsigned int        lifescore			= 50000;				// Number of points needed to earn a 1-up
692 unsigned int        credscore			= 0;					// Number of points needed to earn a credit
693 int                 mpblock				= 0;					// Take chip damage from health or MP first?
694 int                 blockratio			= 0;					// Take half-damage while blocking?
695 int                 nochipdeath			= 0;					// Prevents entities from dying due to chip damage (damage while blocking)
696 int                 noaircancel         = 0;					// Now, you can make jumping attacks uncancellable!
697 int                 nomaxrushreset[5]   = {0, 0, 0, 0, 0};
698 int			        mpbartext[4]		= { -1, 0, 0, 0};			// Array for adjusting MP status text (font, Xpos, Ypos, Display type).
699 int			        lbartext[4]			= { -1, 0, 0, 0};			// Array for adjusting HP status text (font, Xpos, Ypos, Display type).
700 int                 pmp[4][2]			= {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player mpbar
701 int                 spdirection[4]		= {1, 0, 1, 0};			// Used for Select Player Direction for select player screen
702 int                 bonus				= 0;					// Used for unlocking Bonus difficulties
703 int                 versusdamage		= 2;					// Used for setting mode. (ability to hit other players)
704 int                 z_coords[3]			= {0, 0, 0};				// Used for setting customizable walkable area
705 int                 rush[6]				= {0, 2, 3, 3, 3, 3};
706 int                 pauseoffset[7]  	= {0, 1, 0, 0, 3, 0, 0};		// Used for customizable pause menu location (font0, font1, xpos, ypos, font_pause, xpos_pause, ypos_pause)
707 int                 color_black			= 0;
708 int                 color_red			= 0;
709 int                 color_orange		= 0;
710 int                 color_yellow		= 0;
711 int                 color_white			= 0;
712 int                 color_blue			= 0;
713 int                 color_green			= 0;
714 int                 color_pink			= 0;
715 int                 color_purple		= 0;
716 int                 color_magic			= 0;
717 int                 color_magic2		= 0;
718 int                 lifebarfgalpha      = 0;
719 int                 lifebarbgalpha      = 2;
720 int                 shadowsprites[6]	= { -1, -1, -1, -1, -1, -1};
721 int                 gosprite			= -1;
722 int                 golsprite			= -1;
723 //int                 holesprite			= -1;
724 int                 videoMode			= 0;
725 int                 scoreformat			= 0;					// If set fill score values with 6 Zeros
726 
727 // Funny neon lights
728 unsigned char       neontable[MAX_PAL_SIZE];
729 unsigned int        neon_time			= 0;
730 
731 int                 panel_width			= 0;
732 int                 panel_height		= 0;
733 int                 frontpanels_loaded	= 0;
734 
735 unsigned int        sprites_loaded		= 0;
736 unsigned int        anims_loaded		= 0;
737 
738 unsigned int        models_loaded		= 0;
739 unsigned int        models_cached		= 0;
740 
741 entity            **ent_list;
742 entity            **ent_stack; //temporary list, reference only
743 int					ent_stack_size = 0;
744 entity             *self;
745 int                 ent_count			= 0;					// log count of entites
746 int                 ent_max				= 0;
747 
748 s_player            player[MAX_PLAYERS];
749 u64                 bothkeys, bothnewkeys;
750 
751 s_playercontrols    playercontrols1 = {0, 0, 0};
752 s_playercontrols    playercontrols2 = {1, 0, 0};
753 s_playercontrols    playercontrols3 = {2, 0, 0};
754 s_playercontrols    playercontrols4 = {3, 0, 0};
755 s_playercontrols   *playercontrolpointers[] = {&playercontrols1, &playercontrols2, &playercontrols3, &playercontrols4};
756 s_playercontrols    default_control;
757 int default_keys[MAX_BTN_NUM];
758 
759 //global script
760 Script level_script;		//execute when level start
761 Script endlevel_script;		//execute when level finished
762 Script update_script;		//execute when ingame update
763 Script updated_script;		//execute when ingame update finished
764 Script loading_script;		// in loading screen
765 Script input_script_all;  //keyscript for all players
766 Script key_script_all;		//keyscript for all players
767 Script timetick_script;		//time tick script.
768 
769 //player script
770 Script score_script[MAX_PLAYERS];     //execute when add score, 4 players
771 Script key_script[MAX_PLAYERS];       //key listeners
772 Script join_script[MAX_PLAYERS];      //player join scripts
773 Script respawn_script[MAX_PLAYERS];   //player respawn scripts
774 Script pdie_script[MAX_PLAYERS];      //player death scripts
775 
776 extern Script *pcurrentscript;//used by local script functions
777 //-------------------------methods-------------------------------
778 
setDrawMethod(s_anim * a,ptrdiff_t index,s_drawmethod * m)779 void setDrawMethod(s_anim *a, ptrdiff_t index, s_drawmethod *m)
780 {
781     assert(index >= 0);
782     assert(a != NULL);
783     assert(m != NULL);
784     assert(index < a->numframes);
785     a->drawmethods[index] = m;
786 }
787 
getDrawMethod(s_anim * a,ptrdiff_t index)788 s_drawmethod *getDrawMethod(s_anim *a, ptrdiff_t index)
789 {
790     assert(index >= 0);
791     assert(a != NULL);
792     assert(index < a->numframes);
793     return a->drawmethods[index];
794 }
795 
isLoadingScreenTypeBg(e_loadingScreenType what)796 int isLoadingScreenTypeBg(e_loadingScreenType what)
797 {
798     return (what & LS_TYPE_BACKGROUND) == LS_TYPE_BACKGROUND;
799 }
800 
isLoadingScreenTypeBar(e_loadingScreenType what)801 int isLoadingScreenTypeBar(e_loadingScreenType what)
802 {
803     return (what & LS_TYPE_BAR) == LS_TYPE_BAR;
804 }
805 
fill_s_loadingbar(s_loadingbar * s,e_loadingScreenType set,int bx,int by,int bsize,int tx,int ty,int tf,int ms)806 char *fill_s_loadingbar(s_loadingbar *s, e_loadingScreenType set, int bx, int by, int bsize, int tx, int ty, int tf, int ms)
807 {
808     switch (set)
809     {
810         case LS_TYPE_BOTH:
811             s->set = (LS_TYPE_BACKGROUND | LS_TYPE_BAR);
812             break;
813         case LS_TYPE_BACKGROUND:
814             s->set = LS_TYPE_BACKGROUND;
815             break;
816         case LS_TYPE_BAR:
817             s->set = LS_TYPE_BAR;
818             break;
819         case LS_TYPE_NONE:
820             s->set = LS_TYPE_NONE;
821             break;
822         default:
823             s->set = LS_TYPE_NONE;
824             printf("invalid loadingbg type %d!\n", set);
825     }
826     s->tf = tf;
827     s->bar_position.x = bx;
828     s->bar_position.y = by;
829     s->bsize = bsize;
830     s->text_position.x = tx;
831     s->text_position.y = ty;
832     s->refreshMs = (ms ? ms : 100);
833     return NULL;
834 }
835 
836 
buffer_file(char * filename,char ** pbuffer,size_t * psize)837 static int buffer_file(char *filename, char **pbuffer, size_t *psize)
838 {
839     FILE *handle;
840     *psize = 0;
841     *pbuffer = NULL;
842     // Read file
843 #ifdef VERBOSE
844     printf("file requested: %s.\n", filename);
845 #endif
846 
847     if(!(handle = fopen(filename, "rb")) )
848     {
849 #ifdef VERBOSE
850         printf("couldnt get handle!\n");
851 #endif
852         return 0;
853     }
854     fseek(handle, 0, SEEK_END);
855     *psize = ftell(handle);
856     fseek(handle, 0, SEEK_SET);
857 
858     *pbuffer = (char *)malloc(*psize + 1);
859     if(*pbuffer == NULL)
860     {
861         *psize = 0;
862         fclose(handle);
863         borShutdown(1, "Can't create buffer for file '%s'", filename);
864         return 0;
865     }
866     if(fread(*pbuffer, 1, *psize, handle) != *psize)
867     {
868         if(*pbuffer != NULL)
869         {
870             free(*pbuffer);
871             *pbuffer = NULL;
872             *psize = 0;
873         }
874         fclose(handle);
875         borShutdown(1, "Can't read from file '%s'", filename);
876         return 0;
877     }
878     (*pbuffer)[*psize] = 0;        // Terminate string (important!)
879     fclose(handle);
880     return 1;
881 }
882 
883 
884 // returns: 1 - succeeded 0 - failed
buffer_pakfile(char * filename,char ** pbuffer,size_t * psize)885 int buffer_pakfile(char *filename, char **pbuffer, size_t *psize)
886 {
887     int handle;
888     *psize = 0;
889     *pbuffer = NULL;
890 
891     if(buffer_file(filename, pbuffer, psize) == 1)
892     {
893         return 1;
894     }
895 
896     // Read file
897 #ifdef VERBOSE
898     printf("pakfile requested: %s.\n", filename); //ASDF
899 #endif
900 
901     if((handle = openpackfile(filename, packfile)) < 0)
902     {
903 #ifdef VERBOSE
904         printf("couldnt get handle!\n");
905 #endif
906         return 0;
907     }
908     *psize = seekpackfile(handle, 0, SEEK_END);
909     seekpackfile(handle, 0, SEEK_SET);
910 
911     *pbuffer = (char *)malloc(*psize + 1);
912     if(*pbuffer == NULL)
913     {
914         *psize = 0;
915         closepackfile(handle);
916         borShutdown(1, "Can't create buffer for packfile '%s'", filename);
917         return 0;
918     }
919     if(readpackfile(handle, *pbuffer, *psize) != *psize)
920     {
921         if(*pbuffer != NULL)
922         {
923             free(*pbuffer);
924             *pbuffer = NULL;
925             *psize = 0;
926         }
927         closepackfile(handle);
928         borShutdown(1, "Can't read from packfile '%s'", filename);
929         return 0;
930     }
931     (*pbuffer)[*psize] = 0;        // Terminate string (important!)
932     closepackfile(handle);
933     return 1;
934 }
935 
buffer_append(char ** buffer,const char * str,size_t n,size_t * bufferlen,size_t * len)936 int buffer_append(char **buffer, const char *str, size_t n, size_t *bufferlen, size_t *len)
937 {
938     size_t appendlen = strlen(str);
939     if(appendlen > n)
940     {
941         appendlen = n;
942     }
943     if(appendlen + *len + 1 > *bufferlen)
944     {
945         //printf("*Debug* reallocating buffer...\n");
946         *buffer = realloc(*buffer, *bufferlen = appendlen + *len + 1024);
947         if(*buffer == NULL)
948         {
949             borShutdown(1, "Unalbe to resize buffer.\n");
950         }
951     }
952     strncpy(*buffer + *len, str, appendlen);
953     *len = *len + appendlen;
954     (*buffer)[*len] = 0;
955     return *len;
956 }
957 
handle_txt_include(char * command,ArgList * arglist,char ** fn,char * namebuf,char ** buf,ptrdiff_t * pos,size_t * len)958 int handle_txt_include(char *command, ArgList *arglist, char **fn, char *namebuf, char **buf, ptrdiff_t *pos, size_t *len)
959 {
960     char *incfile, *filename = *fn, *buf2, *endstr = "\r\n@end";
961     size_t size, t;
962     if(stricmp(command, "@include") == 0)
963     {
964         incfile = GET_ARGP(1);
965         buffer_pakfile(incfile, &buf2, &size) ;
966         if(buf2)
967         {
968             *buf = realloc(*buf, *len + size + strlen(incfile) + strlen(filename) + 100); //leave enough memory for jump command
969             if(*buf == NULL)
970             {
971                 borShutdown(1, "Unalbe to resize buffer. (handle_txt_include)\n");
972                 free(buf2);
973                 return 0;
974             }
975             sprintf((*buf) + *len - 1, "%s\r\n@filename %s\r\n", endstr, incfile);
976             strcat((*buf) + *len, buf2);
977             t = strlen(*buf);
978             sprintf((*buf) + t, "\r\n@filename %s\r\n@jump %d\r\n", filename, (int)(*pos));
979             (*buf)[*pos] = '#';
980             *pos = *len + strlen(endstr); //continue from the new file position
981             *len = strlen(*buf);
982             free(buf2);
983             //printf(*buf);
984             return 1;
985         }
986         borShutdown(1, "Can't find file '%s' to include.\n", incfile);
987     }
988     else if(stricmp(command, "@jump") == 0)
989     {
990         *pos = GET_INT_ARGP(1);
991         return 2;
992     }
993     else if(stricmp(command, "@end") == 0)
994     {
995         *pos = *len;
996         return 3;
997     }
998     else if(stricmp(command, "@filename") == 0)
999     {
1000         strcpy(namebuf, GET_ARGP(1));
1001         *fn = namebuf;
1002         return 4;
1003     }
1004     return 0;
1005 }
1006 
load_script(Script * script,char * file)1007 int load_script(Script *script, char *file)
1008 {
1009     size_t size = 0;
1010     int failed = 0;
1011     char *buf = NULL;
1012 
1013     if(buffer_pakfile(file, &buf, &size) != 1)
1014     {
1015         return 0;
1016     }
1017 
1018     failed = !Script_AppendText(script, buf, file);
1019 
1020     if(buf != NULL)
1021     {
1022         free(buf);
1023         buf = NULL;
1024     }
1025 
1026     // text loaded but parsing failed, shutdown
1027     if(failed)
1028     {
1029         borShutdown(1, "Failed to parse script file: '%s'!\n", file);
1030     }
1031     return !failed;
1032 }
1033 
1034 // this method is used by load_scripts, don't call it
init_scripts()1035 void init_scripts()
1036 {
1037     int i;
1038     Script_Global_Init();
1039     Script_Init(&update_script,     "update",  NULL,  1);
1040     Script_Init(&updated_script,    "updated",  NULL, 1);
1041     Script_Init(&level_script,      "level",    NULL,  1);
1042     Script_Init(&endlevel_script,   "endlevel",  NULL, 1);
1043 	Script_Init(&input_script_all, "inputall", NULL, 1);
1044     Script_Init(&key_script_all,    "keyall",   NULL,  1);
1045     Script_Init(&timetick_script,   "timetick",  NULL, 1);
1046     Script_Init(&loading_script,    "loading",   NULL, 1);
1047     for(i = 0; i < MAX_PLAYERS; i++)
1048     {
1049         Script_Init(&score_script[i],    "score",    NULL,  1);
1050     }
1051     for(i = 0; i < MAX_PLAYERS; i++)
1052     {
1053         Script_Init(&key_script[i],      "key",      NULL,  1);
1054     }
1055     for(i = 0; i < MAX_PLAYERS; i++)
1056     {
1057         Script_Init(&join_script[i],     "join",      NULL, 1);
1058     }
1059     for(i = 0; i < MAX_PLAYERS; i++)
1060     {
1061         Script_Init(&respawn_script[i],  "respawn",   NULL, 1);
1062     }
1063     for(i = 0; i < MAX_PLAYERS; i++)
1064     {
1065         Script_Init(&pdie_script[i],     "die",       NULL, 1);
1066     }
1067 }
1068 
1069 // This method is called once when the engine starts, do not use it multiple times
1070 // It should be calld after load_script_setting
load_scripts()1071 void load_scripts()
1072 {
1073     int i;
1074     init_scripts();
1075     //Script_Clear's second parameter set to 2, because the script fails to load,
1076     //and will never have another chance to be loaded, so just clear the variable list in it
1077     if(!load_script(&update_script,     "data/scripts/update.c"))
1078     {
1079         Script_Clear(&update_script,        2);
1080     }
1081     if(!load_script(&updated_script,    "data/scripts/updated.c"))
1082     {
1083         Script_Clear(&updated_script,       2);
1084     }
1085     if(!load_script(&level_script,      "data/scripts/level.c"))
1086     {
1087         Script_Clear(&level_script,         2);
1088     }
1089     if(!load_script(&endlevel_script,   "data/scripts/endlevel.c"))
1090     {
1091         Script_Clear(&endlevel_script,      2);
1092     }
1093 	if (!load_script(&input_script_all, "data/scripts/inputall.c"))
1094 	{
1095 		Script_Clear(&input_script_all, 2);
1096 	}
1097     if(!load_script(&key_script_all,    "data/scripts/keyall.c"))
1098     {
1099         Script_Clear(&key_script_all,       2);
1100     }
1101     if(!load_script(&timetick_script,   "data/scripts/timetick.c"))
1102     {
1103         Script_Clear(&timetick_script,      2);
1104     }
1105     if(!load_script(&loading_script,    "data/scripts/loading.c"))
1106     {
1107         Script_Clear(&loading_script,       2);
1108     }
1109     if(!load_script(&score_script[0],   "data/scripts/score1.c"))
1110     {
1111         Script_Clear(&score_script[0],      2);
1112     }
1113     if(!load_script(&score_script[1],   "data/scripts/score2.c"))
1114     {
1115         Script_Clear(&score_script[1],      2);
1116     }
1117     if(!load_script(&score_script[2],   "data/scripts/score3.c"))
1118     {
1119         Script_Clear(&score_script[2],      2);
1120     }
1121     if(!load_script(&score_script[3],   "data/scripts/score4.c"))
1122     {
1123         Script_Clear(&score_script[3],      2);
1124     }
1125     if(!load_script(&key_script[0],     "data/scripts/key1.c"))
1126     {
1127         Script_Clear(&key_script[0],        2);
1128     }
1129     if(!load_script(&key_script[1],     "data/scripts/key2.c"))
1130     {
1131         Script_Clear(&key_script[1],        2);
1132     }
1133     if(!load_script(&key_script[2],     "data/scripts/key3.c"))
1134     {
1135         Script_Clear(&key_script[2],        2);
1136     }
1137     if(!load_script(&key_script[3],     "data/scripts/key4.c"))
1138     {
1139         Script_Clear(&key_script[3],        2);
1140     }
1141     if(!load_script(&join_script[0],    "data/scripts/join1.c"))
1142     {
1143         Script_Clear(&join_script[0],       2);
1144     }
1145     if(!load_script(&join_script[1],    "data/scripts/join2.c"))
1146     {
1147         Script_Clear(&join_script[1],       2);
1148     }
1149     if(!load_script(&join_script[2],    "data/scripts/join3.c"))
1150     {
1151         Script_Clear(&join_script[2],       2);
1152     }
1153     if(!load_script(&join_script[3],    "data/scripts/join4.c"))
1154     {
1155         Script_Clear(&join_script[3],       2);
1156     }
1157     if(!load_script(&respawn_script[0], "data/scripts/respawn1.c"))
1158     {
1159         Script_Clear(&respawn_script[0],    2);
1160     }
1161     if(!load_script(&respawn_script[1], "data/scripts/respawn2.c"))
1162     {
1163         Script_Clear(&respawn_script[1],    2);
1164     }
1165     if(!load_script(&respawn_script[2], "data/scripts/respawn3.c"))
1166     {
1167         Script_Clear(&respawn_script[2],    2);
1168     }
1169     if(!load_script(&respawn_script[3], "data/scripts/respawn4.c"))
1170     {
1171         Script_Clear(&respawn_script[3],    2);
1172     }
1173     if(!load_script(&pdie_script[0],    "data/scripts/die1.c"))
1174     {
1175         Script_Clear(&pdie_script[0],       2);
1176     }
1177     if(!load_script(&pdie_script[1],    "data/scripts/die2.c"))
1178     {
1179         Script_Clear(&pdie_script[1],       2);
1180     }
1181     if(!load_script(&pdie_script[2],    "data/scripts/die3.c"))
1182     {
1183         Script_Clear(&pdie_script[2],       2);
1184     }
1185     if(!load_script(&pdie_script[3],    "data/scripts/die4.c"))
1186     {
1187         Script_Clear(&pdie_script[3],       2);
1188     }
1189     Script_Compile(&update_script);
1190     Script_Compile(&updated_script);
1191     Script_Compile(&level_script);
1192     Script_Compile(&endlevel_script);
1193 	Script_Compile(&input_script_all);
1194     Script_Compile(&key_script_all);
1195     Script_Compile(&timetick_script);
1196     Script_Compile(&loading_script);
1197     for(i = 0; i < MAX_PLAYERS; i++)
1198     {
1199         Script_Compile(&score_script[i]);
1200     }
1201     for(i = 0; i < MAX_PLAYERS; i++)
1202     {
1203         Script_Compile(&key_script[i]);
1204     }
1205     for(i = 0; i < MAX_PLAYERS; i++)
1206     {
1207         Script_Compile(&join_script[i]);
1208     }
1209     for(i = 0; i < MAX_PLAYERS; i++)
1210     {
1211         Script_Compile(&respawn_script[i]);
1212     }
1213     for(i = 0; i < MAX_PLAYERS; i++)
1214     {
1215         Script_Compile(&pdie_script[i]);
1216     }
1217 }
1218 
unfrozen(entity * e)1219 void unfrozen(entity *e)
1220 {
1221     ent_set_colourmap(e, e->map);
1222     e->frozen = 0;
1223     e->freezetime = 0;
1224 }
1225 
is_frozen(entity * e)1226 int is_frozen(entity *e)
1227 {
1228     return ((textbox && e->modeldata.type != TYPE_TEXTBOX) ||
1229 						 (smartbomber && e != smartbomber && e->modeldata.type != TYPE_TEXTBOX) || (self->frozen && self->freezetime > _time));
1230 }
1231 
1232 // This method is called once when the engine is shutting down, do not use it multiple times
clear_scripts()1233 void clear_scripts()
1234 {
1235     int i;
1236     //Script_Clear's second parameter set to 2, because the script fails to load,
1237     //and will never have another chance to be loaded, so just clear the variable list in it
1238     Script_Clear(&update_script,    2);
1239     Script_Clear(&updated_script,   2);
1240     Script_Clear(&level_script,     2);
1241     Script_Clear(&endlevel_script,  2);
1242 	Script_Clear(&input_script_all, 2);
1243     Script_Clear(&key_script_all,   2);
1244     Script_Clear(&timetick_script,  2);
1245     Script_Clear(&loading_script,   2);
1246     for(i = 0; i < MAX_PLAYERS; i++)
1247     {
1248         Script_Clear(&score_script[i],      2);
1249     }
1250     for(i = 0; i < MAX_PLAYERS; i++)
1251     {
1252         Script_Clear(&key_script[i],        2);
1253     }
1254     for(i = 0; i < MAX_PLAYERS; i++)
1255     {
1256         Script_Clear(&join_script[i],       2);
1257     }
1258     for(i = 0; i < MAX_PLAYERS; i++)
1259     {
1260         Script_Clear(&respawn_script[i],    2);
1261     }
1262     for(i = 0; i < MAX_PLAYERS; i++)
1263     {
1264         Script_Clear(&pdie_script[i],       2);
1265     }
1266     Script_Global_Clear();
1267 }
1268 
1269 #define scripts_membercount (sizeof(s_scripts) / sizeof(Script*))
1270 
alloc_all_scripts(s_scripts ** s)1271 void alloc_all_scripts(s_scripts **s)
1272 {
1273     size_t i;
1274 
1275     if(!(*s))
1276     {
1277         *s = (s_scripts *)malloc(sizeof(s_scripts));
1278         for (i = 0; i < scripts_membercount; i++)
1279         {
1280             (((Script **) (*s))[i]) = alloc_script();
1281         }
1282     }
1283 }
1284 
clear_all_scripts(s_scripts * s,int method)1285 void clear_all_scripts(s_scripts *s, int method)
1286 {
1287     size_t i;
1288     Script **ps = (Script **) s;
1289 
1290     for (i = 0; i < scripts_membercount; i++)
1291     {
1292         Script_Clear(ps[i],   method);
1293     }
1294 }
1295 
free_all_scripts(s_scripts ** s)1296 void free_all_scripts(s_scripts **s)
1297 {
1298     size_t i;
1299     Script **ps = (Script **) (*s);
1300 
1301     for (i = 0; i < scripts_membercount; i++)
1302     {
1303         if (ps[i])
1304         {
1305             free(ps[i]);
1306             ps[i] = NULL;
1307         }
1308     }
1309     free(*s);
1310     *s = NULL;
1311 }
1312 
copy_all_scripts(s_scripts * src,s_scripts * dest,int method)1313 void copy_all_scripts(s_scripts *src, s_scripts *dest, int method)
1314 {
1315     size_t i;
1316     Script **ps = (Script **) src;
1317     Script **pd = (Script **) dest;
1318 
1319     for (i = 0; i < scripts_membercount; i++)
1320     {
1321         Script_Copy(pd[i], ps[i], method);
1322     }
1323 }
1324 
execute_animation_script(entity * ent)1325 void execute_animation_script(entity *ent)
1326 {
1327     ScriptVariant tempvar;
1328     int is1 = 0, is2 = 0;
1329     char *namelist[] = {"self", "animnum", "frame", "animhandle", ""};
1330     int handle = 0;
1331     Script *cs = ent->scripts->animation_script;
1332     Script *s1 = ent->model->scripts->animation_script;
1333     Script *s2 = ent->defaultmodel->scripts->animation_script;
1334     is1 = Script_IsInitialized(s1);
1335     is2 = Script_IsInitialized(s2);
1336     if(is1 || is2)
1337     {
1338         if(cs->pinterpreter && cs->pinterpreter->bReset)
1339         {
1340             handle = Script_Save_Local_Variant(cs, namelist);
1341         }
1342         ScriptVariant_Init(&tempvar);
1343         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1344         tempvar.ptrVal = (VOID *)ent;
1345         Script_Set_Local_Variant(cs, "self",    &tempvar);
1346         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1347         tempvar.lVal = (LONG)ent->animnum;
1348         Script_Set_Local_Variant(cs, "animnum", &tempvar);
1349         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1350         tempvar.lVal = (LONG)ent->animpos;
1351         Script_Set_Local_Variant(cs, "frame",   &tempvar);
1352         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1353         tempvar.lVal = (LONG)ent->animation->index;
1354         Script_Set_Local_Variant(cs, "animhandle",   &tempvar);
1355         if(is1)
1356         {
1357             Script_Copy(cs, s1, 0);
1358             Script_Execute(cs);
1359         }
1360         if(ent->model != ent->defaultmodel && is2)
1361         {
1362             Script_Copy(cs, s2, 0);
1363             Script_Execute(cs);
1364         }
1365         //clear to save variant space
1366         ScriptVariant_Clear(&tempvar);
1367         Script_Set_Local_Variant(cs, "self",    &tempvar);
1368         Script_Set_Local_Variant(cs, "animnum", &tempvar);
1369         Script_Set_Local_Variant(cs, "frame",   &tempvar);
1370         Script_Set_Local_Variant(cs, "animhandle", &tempvar);
1371         if(handle)
1372         {
1373             Script_Load_Local_Variant(cs, handle);
1374         }
1375     }
1376 }
1377 
execute_takedamage_script(entity * ent,entity * other,s_collision_attack * attack)1378 void execute_takedamage_script(entity *ent, entity *other, s_collision_attack *attack)
1379 {
1380     ScriptVariant tempvar;
1381     Script *cs = ent->scripts->takedamage_script;
1382     if(Script_IsInitialized(cs))
1383     {
1384         ScriptVariant_Init(&tempvar);
1385         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1386 
1387         tempvar.ptrVal = (VOID *)ent;
1388         Script_Set_Local_Variant(cs, "self",        &tempvar);
1389 
1390         tempvar.ptrVal = (VOID *)other;
1391         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1392 
1393         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1394 
1395         tempvar.lVal = (LONG)attack->attack_force;
1396         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1397 
1398         tempvar.lVal = (LONG)attack->attack_drop;
1399         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1400 
1401         tempvar.lVal = (LONG)attack->attack_type;
1402         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1403 
1404         tempvar.lVal = (LONG)attack->no_block;
1405         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1406 
1407         tempvar.lVal = (LONG)attack->guardcost;
1408         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1409 
1410         tempvar.lVal = (LONG)attack->jugglecost;
1411         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1412 
1413         tempvar.lVal = (LONG)attack->pause_add;
1414         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1415 
1416         tempvar.lVal = (LONG)attack->tag;
1417         Script_Set_Local_Variant(cs, "tag",    &tempvar);
1418 
1419 
1420         Script_Execute(cs);
1421 
1422         //clear to save variant space
1423         ScriptVariant_Clear(&tempvar);
1424         Script_Set_Local_Variant(cs, "self",        &tempvar);
1425         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1426         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1427         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1428         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1429         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1430         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1431         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1432         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1433         Script_Set_Local_Variant(cs, "tag",    &tempvar);
1434     }
1435 }
1436 
1437 // Caskey, Damon V.
1438 // 2018-08-30
1439 //
1440 // Run on the bind target when updating a bind.
execute_on_bind_update_other_to_self(entity * ent,entity * other,s_bind * bind)1441 void execute_on_bind_update_other_to_self(entity *ent, entity *other, s_bind *bind)
1442 {
1443     ScriptVariant tempvar;
1444     Script *cs = ent->scripts->on_bind_update_other_to_self_script;
1445 
1446     if(Script_IsInitialized(cs))
1447     {
1448         ScriptVariant_Init(&tempvar);
1449         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1450 
1451         tempvar.ptrVal = (entity *)ent;
1452         Script_Set_Local_Variant(cs, "self", &tempvar);
1453 
1454         tempvar.ptrVal = (entity *)other;
1455         Script_Set_Local_Variant(cs, "other", &tempvar);
1456 
1457         tempvar.ptrVal = (s_bind *)bind;
1458         Script_Set_Local_Variant(cs, "bind", &tempvar);
1459 
1460         Script_Execute(cs);
1461 
1462         //clear to save variant space
1463         ScriptVariant_Clear(&tempvar);
1464         Script_Set_Local_Variant(cs, "self",	&tempvar);
1465         Script_Set_Local_Variant(cs, "other",	&tempvar);
1466         Script_Set_Local_Variant(cs, "bind",	&tempvar);
1467     }
1468 }
1469 
1470 // Caskey, Damon V.
1471 // 2018-08-30
1472 //
1473 // Run on bound entity when updating bind.
execute_on_bind_update_self_to_other(entity * ent,entity * other,s_bind * bind)1474 void execute_on_bind_update_self_to_other(entity *ent, entity *other, s_bind *bind)
1475 {
1476     ScriptVariant tempvar;
1477     Script *cs = ent->scripts->on_bind_update_self_to_other_script;
1478 
1479     if(Script_IsInitialized(cs))
1480     {
1481         ScriptVariant_Init(&tempvar);
1482         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1483 
1484         tempvar.ptrVal = (entity *)ent;
1485         Script_Set_Local_Variant(cs, "self", &tempvar);
1486 
1487         tempvar.ptrVal = (entity *)other;
1488         Script_Set_Local_Variant(cs, "other", &tempvar);
1489 
1490         tempvar.ptrVal = (s_bind *)bind;
1491         Script_Set_Local_Variant(cs, "bind", &tempvar);
1492 
1493         Script_Execute(cs);
1494 
1495         //clear to save variant space
1496         ScriptVariant_Clear(&tempvar);
1497         Script_Set_Local_Variant(cs, "self",	&tempvar);
1498         Script_Set_Local_Variant(cs, "other",	&tempvar);
1499         Script_Set_Local_Variant(cs, "bind",	&tempvar);
1500     }
1501 }
1502 
execute_onpain_script(entity * ent,int iType,int iReset)1503 void execute_onpain_script(entity *ent, int iType, int iReset)
1504 {
1505     ScriptVariant tempvar;
1506     Script *cs = ent->scripts->onpain_script;
1507     if(Script_IsInitialized(cs))
1508     {
1509         ScriptVariant_Init(&tempvar);
1510         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1511         tempvar.ptrVal = (VOID *)ent;
1512         Script_Set_Local_Variant(cs, "self",        &tempvar);
1513         tempvar.lVal = (LONG)iType;
1514         Script_Set_Local_Variant(cs, "attacktype",   &tempvar);
1515         tempvar.lVal = (LONG)iReset;
1516         Script_Set_Local_Variant(cs, "reset",       &tempvar);
1517         Script_Execute(cs);
1518         //clear to save variant space
1519         ScriptVariant_Clear(&tempvar);
1520         Script_Set_Local_Variant(cs, "self",        &tempvar);
1521         Script_Set_Local_Variant(cs, "type",        &tempvar);
1522         Script_Set_Local_Variant(cs, "reset",       &tempvar);
1523     }
1524 }
1525 
execute_onfall_script(entity * ent,entity * other,s_collision_attack * attack)1526 void execute_onfall_script(entity *ent, entity *other, s_collision_attack *attack)
1527 {
1528     ScriptVariant tempvar;
1529     Script *cs = ent->scripts->onfall_script;
1530     if(Script_IsInitialized(cs))
1531     {
1532         ScriptVariant_Init(&tempvar);
1533         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1534         tempvar.ptrVal = (VOID *)ent;
1535         Script_Set_Local_Variant(cs, "self",        &tempvar);
1536 
1537         tempvar.ptrVal = (VOID *)other;
1538         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1539 
1540         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1541 
1542         tempvar.lVal = (LONG)attack->attack_force;
1543         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1544 
1545         tempvar.lVal = (LONG)attack->attack_drop;
1546         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1547 
1548         tempvar.lVal = (LONG)attack->attack_type;
1549         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1550 
1551         tempvar.lVal = (LONG)attack->no_block;
1552         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1553 
1554         tempvar.lVal = (LONG)attack->guardcost;
1555         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1556 
1557         tempvar.lVal = (LONG)attack->jugglecost;
1558         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1559 
1560         tempvar.lVal = (LONG)attack->pause_add;
1561         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1562 
1563         tempvar.lVal = (LONG)attack->tag;
1564         Script_Set_Local_Variant(cs, "tag",    &tempvar);
1565 
1566         Script_Execute(cs);
1567         //clear to save variant space
1568         ScriptVariant_Clear(&tempvar);
1569         Script_Set_Local_Variant(cs, "self",        &tempvar);
1570         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1571         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1572         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1573         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1574         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1575         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1576         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1577         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1578         Script_Set_Local_Variant(cs, "tag",         &tempvar);
1579     }
1580 }
1581 
execute_onblocks_script(entity * ent)1582 void execute_onblocks_script(entity *ent)
1583 {
1584     ScriptVariant tempvar;
1585     Script *cs = ent->scripts->onblocks_script;
1586     if(Script_IsInitialized(cs))
1587     {
1588         ScriptVariant_Init(&tempvar);
1589         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1590         tempvar.ptrVal = (VOID *)ent;
1591         Script_Set_Local_Variant(cs, "self", &tempvar);
1592         Script_Execute(cs);
1593 
1594         //clear to save variant space
1595         ScriptVariant_Clear(&tempvar);
1596         Script_Set_Local_Variant(cs, "self", &tempvar);
1597     }
1598 }
1599 
execute_onblockw_script(entity * ent,s_terrain * wall,int index,e_plane plane)1600 void execute_onblockw_script(entity *ent, s_terrain *wall, int index, e_plane plane)
1601 {
1602     ScriptVariant tempvar;
1603     Script *cs = ent->scripts->onblockw_script;
1604     if(Script_IsInitialized(cs))
1605     {
1606         ScriptVariant_Init(&tempvar);
1607         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1608         tempvar.ptrVal = (VOID *)ent;
1609         Script_Set_Local_Variant(cs, "self", &tempvar);
1610 
1611         ScriptVariant_ChangeType(&tempvar, VT_DECIMAL);
1612         tempvar.dblVal = (DOUBLE)wall->height;
1613         Script_Set_Local_Variant(cs, "height", &tempvar);
1614 
1615         tempvar.dblVal = (DOUBLE)wall->depth;
1616         Script_Set_Local_Variant(cs, "depth", &tempvar);
1617 
1618         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1619 
1620         tempvar.lVal = (LONG)wall->type;
1621         Script_Set_Local_Variant(cs, "type", &tempvar);
1622 
1623         tempvar.lVal = (LONG)index;
1624         Script_Set_Local_Variant(cs, "index", &tempvar);
1625 
1626         tempvar.lVal = (LONG)plane;
1627         Script_Set_Local_Variant(cs, "plane", &tempvar);
1628 
1629         Script_Execute(cs);
1630 
1631         //clear to save variant space
1632         ScriptVariant_Clear(&tempvar);
1633         Script_Set_Local_Variant(cs, "self", &tempvar);
1634         Script_Set_Local_Variant(cs, "plane", &tempvar);
1635         Script_Set_Local_Variant(cs, "height", &tempvar);
1636         Script_Set_Local_Variant(cs, "index", &tempvar);
1637         Script_Set_Local_Variant(cs, "depth", &tempvar);
1638         Script_Set_Local_Variant(cs, "type", &tempvar);
1639     }
1640 }
1641 
execute_inhole_script(entity * ent,s_terrain * hole,int index)1642 void execute_inhole_script(entity *ent, s_terrain *hole, int index)
1643 {
1644     ScriptVariant tempvar;
1645     Script *cs = ent->scripts->inhole_script;
1646     if(Script_IsInitialized(cs))
1647     {
1648         ScriptVariant_Init(&tempvar);
1649         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1650         tempvar.ptrVal = (VOID *)ent;
1651         Script_Set_Local_Variant(cs, "self", &tempvar);
1652 
1653         ScriptVariant_ChangeType(&tempvar, VT_DECIMAL);
1654         tempvar.dblVal = (DOUBLE)hole->height;
1655         Script_Set_Local_Variant(cs, "height", &tempvar);
1656 
1657         tempvar.dblVal = (DOUBLE)hole->depth;
1658         Script_Set_Local_Variant(cs, "depth", &tempvar);
1659 
1660         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1661 
1662         tempvar.lVal = (LONG)hole->type;
1663         Script_Set_Local_Variant(cs, "type", &tempvar);
1664 
1665         tempvar.lVal = (LONG)index;
1666         Script_Set_Local_Variant(cs, "index", &tempvar);
1667 
1668         Script_Execute(cs);
1669 
1670         //clear to save variant space
1671         ScriptVariant_Clear(&tempvar);
1672         Script_Set_Local_Variant(cs, "self", &tempvar);
1673         Script_Set_Local_Variant(cs, "height", &tempvar);
1674         Script_Set_Local_Variant(cs, "index", &tempvar);
1675         Script_Set_Local_Variant(cs, "depth", &tempvar);
1676         Script_Set_Local_Variant(cs, "type", &tempvar);
1677     }
1678 }
1679 
execute_onblockp_script(entity * ent,int plane,entity * platform)1680 void execute_onblockp_script(entity *ent, int plane, entity *platform)
1681 {
1682     ScriptVariant tempvar;
1683     Script *cs = ent->scripts->onblockp_script;
1684     if(Script_IsInitialized(cs))
1685     {
1686         ScriptVariant_Init(&tempvar);
1687         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1688         tempvar.ptrVal = (VOID *)ent;
1689         Script_Set_Local_Variant(cs, "self", &tempvar);
1690         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1691         tempvar.lVal = (LONG)plane;
1692         Script_Set_Local_Variant(cs, "plane",      &tempvar);
1693         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1694         tempvar.ptrVal = (VOID *)platform;
1695         Script_Set_Local_Variant(cs, "platform",      &tempvar);
1696         Script_Execute(cs);
1697 
1698         //clear to save variant space
1699         ScriptVariant_Clear(&tempvar);
1700         Script_Set_Local_Variant(cs, "self", &tempvar);
1701         Script_Set_Local_Variant(cs, "plane", &tempvar);
1702         Script_Set_Local_Variant(cs, "platform", &tempvar);
1703     }
1704 }
1705 
execute_onentitycollision_script(entity * ent,entity * target,s_collision_entity * ebox_ent,s_collision_entity * ebox_target)1706 void execute_onentitycollision_script(entity *ent, entity *target, s_collision_entity *ebox_ent, s_collision_entity *ebox_target)
1707 {
1708     ScriptVariant tempvar;
1709     Script *cs = ent->scripts->onentitycollision_script;
1710     if(Script_IsInitialized(cs))
1711     {
1712         ScriptVariant_Init(&tempvar);
1713         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1714         tempvar.ptrVal = (VOID *)ent;
1715         Script_Set_Local_Variant(cs, "self", &tempvar);
1716 
1717         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1718         tempvar.ptrVal = (VOID *)target;
1719         Script_Set_Local_Variant(cs, "target", &tempvar);
1720 
1721         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1722         tempvar.ptrVal = (VOID *)ebox_ent;
1723         Script_Set_Local_Variant(cs, "self_ebox_handler", &tempvar);
1724 
1725         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1726         tempvar.ptrVal = (VOID *)ebox_target;
1727         Script_Set_Local_Variant(cs, "target_ebox_handler", &tempvar);
1728 
1729         Script_Execute(cs);
1730 
1731         //clear to save variant space
1732         ScriptVariant_Clear(&tempvar);
1733         Script_Set_Local_Variant(cs, "self", &tempvar);
1734         Script_Set_Local_Variant(cs, "target", &tempvar);
1735         Script_Set_Local_Variant(cs, "self_ebox_handler", &tempvar);
1736         Script_Set_Local_Variant(cs, "target_ebox_handler", &tempvar);
1737     }
1738 }
1739 
execute_onblocko_script(entity * ent,int plane,entity * other)1740 void execute_onblocko_script(entity *ent, int plane, entity *other)
1741 {
1742     ScriptVariant tempvar;
1743     Script *cs = ent->scripts->onblocko_script;
1744     if(Script_IsInitialized(cs))
1745     {
1746         ScriptVariant_Init(&tempvar);
1747         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1748         tempvar.ptrVal = (VOID *)ent;
1749         Script_Set_Local_Variant(cs, "self",        &tempvar);
1750         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1751         tempvar.lVal = (LONG)plane;
1752         Script_Set_Local_Variant(cs, "plane",      &tempvar);
1753         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1754         tempvar.ptrVal = (VOID *)other;
1755         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
1756         Script_Execute(cs);
1757 
1758         //clear to save variant space
1759         ScriptVariant_Clear(&tempvar);
1760         Script_Set_Local_Variant(cs, "self",        &tempvar);
1761         Script_Set_Local_Variant(cs, "plane", &tempvar);
1762         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
1763     }
1764 }
1765 
execute_onblockz_script(entity * ent)1766 void execute_onblockz_script(entity *ent)
1767 {
1768     ScriptVariant tempvar;
1769     Script *cs = ent->scripts->onblockz_script;
1770     if(Script_IsInitialized(cs))
1771     {
1772         ScriptVariant_Init(&tempvar);
1773         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1774         tempvar.ptrVal = (VOID *)ent;
1775         Script_Set_Local_Variant(cs, "self", &tempvar);
1776         Script_Execute(cs);
1777 
1778         //clear to save variant space
1779         ScriptVariant_Clear(&tempvar);
1780         Script_Set_Local_Variant(cs, "self", &tempvar);
1781     }
1782 }
1783 
execute_onblocka_script(entity * ent,entity * other)1784 void execute_onblocka_script(entity *ent, entity *other)
1785 {
1786     ScriptVariant tempvar;
1787     Script *cs = ent->scripts->onblocka_script;
1788     if(Script_IsInitialized(cs))
1789     {
1790         ScriptVariant_Init(&tempvar);
1791         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1792         tempvar.ptrVal = (VOID *)ent;
1793         Script_Set_Local_Variant(cs, "self",        &tempvar);
1794         tempvar.ptrVal = (VOID *)other;
1795         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
1796         Script_Execute(cs);
1797         //clear to save variant space
1798         ScriptVariant_Clear(&tempvar);
1799         Script_Set_Local_Variant(cs, "self",        &tempvar);
1800         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
1801     }
1802 }
1803 
execute_onmovex_script(entity * ent)1804 void execute_onmovex_script(entity *ent)
1805 {
1806     ScriptVariant tempvar;
1807     Script *cs = ent->scripts->onmovex_script;
1808     if(Script_IsInitialized(cs))
1809     {
1810         ScriptVariant_Init(&tempvar);
1811         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1812         tempvar.ptrVal = (VOID *)ent;
1813         Script_Set_Local_Variant(cs, "self", &tempvar);
1814         Script_Execute(cs);
1815 
1816         //clear to save variant space
1817         ScriptVariant_Clear(&tempvar);
1818         Script_Set_Local_Variant(cs, "self", &tempvar);
1819     }
1820 }
1821 
execute_onmovez_script(entity * ent)1822 void execute_onmovez_script(entity *ent)
1823 {
1824     ScriptVariant tempvar;
1825     Script *cs = ent->scripts->onmovez_script;
1826     if(Script_IsInitialized(cs))
1827     {
1828         ScriptVariant_Init(&tempvar);
1829         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1830         tempvar.ptrVal = (VOID *)ent;
1831         Script_Set_Local_Variant(cs, "self", &tempvar);
1832         Script_Execute(cs);
1833 
1834         //clear to save variant space
1835         ScriptVariant_Clear(&tempvar);
1836         Script_Set_Local_Variant(cs, "self", &tempvar);
1837     }
1838 }
1839 
execute_onmovea_script(entity * ent)1840 void execute_onmovea_script(entity *ent)
1841 {
1842     ScriptVariant tempvar;
1843     Script *cs = ent->scripts->onmovea_script;
1844     if(Script_IsInitialized(cs))
1845     {
1846         ScriptVariant_Init(&tempvar);
1847         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1848         tempvar.ptrVal = (VOID *)ent;
1849         Script_Set_Local_Variant(cs, "self", &tempvar);
1850         Script_Execute(cs);
1851 
1852         //clear to save variant space
1853         ScriptVariant_Clear(&tempvar);
1854         Script_Set_Local_Variant(cs, "self", &tempvar);
1855     }
1856 }
1857 
execute_ondeath_script(entity * ent,entity * other,s_collision_attack * attack)1858 void execute_ondeath_script(entity *ent, entity *other, s_collision_attack *attack)
1859 {
1860     ScriptVariant tempvar;
1861     Script *cs = ent->scripts->ondeath_script;
1862     if(Script_IsInitialized(cs))
1863     {
1864         ScriptVariant_Init(&tempvar);
1865         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1866 
1867         tempvar.ptrVal = (VOID *)ent;
1868         Script_Set_Local_Variant(cs, "self",        &tempvar);
1869 
1870         tempvar.ptrVal = (VOID *)other;
1871         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1872 
1873         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1874 
1875         tempvar.lVal = (LONG)attack->attack_force;
1876         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1877 
1878         tempvar.lVal = (LONG)attack->attack_drop;
1879         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1880 
1881         tempvar.lVal = (LONG)attack->attack_type;
1882         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1883 
1884         tempvar.lVal = (LONG)attack->no_block;
1885         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1886 
1887         tempvar.lVal = (LONG)attack->guardcost;
1888         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1889 
1890         tempvar.lVal = (LONG)attack->jugglecost;
1891         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1892 
1893         tempvar.lVal = (LONG)attack->pause_add;
1894         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1895 
1896         tempvar.lVal = (LONG)attack->tag;
1897         Script_Set_Local_Variant(cs, "tag",    &tempvar);
1898 
1899         Script_Execute(cs);
1900         //clear to save variant space
1901 
1902         ScriptVariant_Clear(&tempvar);
1903         Script_Set_Local_Variant(cs, "self",        &tempvar);
1904         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1905         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1906         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1907         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1908         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1909         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1910         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1911         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1912         Script_Set_Local_Variant(cs, "tag",         &tempvar);
1913     }
1914 }
1915 
execute_onkill_script(entity * ent)1916 void execute_onkill_script(entity *ent)
1917 {
1918     ScriptVariant tempvar;
1919     Script *cs = ent->scripts->onkill_script;
1920     if(Script_IsInitialized(cs))
1921     {
1922         ScriptVariant_Init(&tempvar);
1923         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1924         tempvar.ptrVal = (VOID *)ent;
1925         Script_Set_Local_Variant(cs, "self", &tempvar);
1926         Script_Execute(cs);
1927         //clear to save variant space
1928         ScriptVariant_Clear(&tempvar);
1929         Script_Set_Local_Variant(cs, "self", &tempvar);
1930     }
1931 }
1932 
execute_didblock_script(entity * ent,entity * other,s_collision_attack * attack)1933 void execute_didblock_script(entity *ent, entity *other, s_collision_attack *attack)
1934 {
1935     ScriptVariant tempvar;
1936     Script *cs = ent->scripts->didblock_script;
1937     if(Script_IsInitialized(cs))
1938     {
1939         ScriptVariant_Init(&tempvar);
1940         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1941 
1942         tempvar.ptrVal = (VOID *)ent;
1943         Script_Set_Local_Variant(cs, "self",        &tempvar);
1944 
1945         tempvar.ptrVal = (VOID *)other;
1946         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1947 
1948         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
1949 
1950         tempvar.lVal = (LONG)attack->attack_force;
1951         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1952 
1953         tempvar.lVal = (LONG)attack->attack_drop;
1954         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1955 
1956         tempvar.lVal = (LONG)attack->attack_type;
1957         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1958 
1959         tempvar.lVal = (LONG)attack->no_block;
1960         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1961 
1962         tempvar.lVal = (LONG)attack->guardcost;
1963         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1964 
1965         tempvar.lVal = (LONG)attack->jugglecost;
1966         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1967 
1968         tempvar.lVal = (LONG)attack->pause_add;
1969         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1970 
1971         tempvar.lVal = (LONG)attack->tag;
1972         Script_Set_Local_Variant(cs, "tag",    &tempvar);
1973 
1974         Script_Execute(cs);
1975         //clear to save variant space
1976         ScriptVariant_Clear(&tempvar);
1977         Script_Set_Local_Variant(cs, "self",        &tempvar);
1978         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
1979         Script_Set_Local_Variant(cs, "damage",      &tempvar);
1980         Script_Set_Local_Variant(cs, "drop",        &tempvar);
1981         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
1982         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
1983         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
1984         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
1985         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
1986         Script_Set_Local_Variant(cs, "tag",         &tempvar);
1987     }
1988 }
1989 
execute_ondoattack_script(entity * ent,entity * other,s_collision_attack * attack,e_exchange which,int attack_id)1990 void execute_ondoattack_script(entity *ent, entity *other, s_collision_attack *attack, e_exchange which, int attack_id)
1991 {
1992     ScriptVariant tempvar;
1993     Script *cs = ent->scripts->ondoattack_script;
1994     if(Script_IsInitialized(cs))
1995     {
1996         ScriptVariant_Init(&tempvar);
1997         ScriptVariant_ChangeType(&tempvar, VT_PTR);
1998         tempvar.ptrVal = (VOID *)ent;
1999         Script_Set_Local_Variant(cs, "self",        &tempvar);
2000 
2001         tempvar.ptrVal = (VOID *)other;
2002         Script_Set_Local_Variant(cs, "other",    &tempvar);
2003 
2004         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2005 
2006         tempvar.lVal = (LONG)attack->attack_force;
2007         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2008 
2009         tempvar.lVal = (LONG)attack->attack_drop;
2010         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2011 
2012         tempvar.lVal = (LONG)attack->attack_type;
2013         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2014 
2015         tempvar.lVal = (LONG)attack->no_block;
2016         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2017 
2018         tempvar.lVal = (LONG)attack->guardcost;
2019         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2020 
2021         tempvar.lVal = (LONG)attack->jugglecost;
2022         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2023 
2024         tempvar.lVal = (LONG)attack->pause_add;
2025         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2026 
2027         tempvar.lVal = (LONG)attack->tag;
2028         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2029 
2030         tempvar.lVal = (LONG)which;
2031         Script_Set_Local_Variant(cs, "which",    &tempvar);
2032 
2033         tempvar.lVal = (LONG)attack_id;
2034         Script_Set_Local_Variant(cs, "attack_id",    &tempvar);
2035 
2036         Script_Execute(cs);
2037         //clear to save variant space
2038         ScriptVariant_Clear(&tempvar);
2039         Script_Set_Local_Variant(cs, "self",        &tempvar);
2040         Script_Set_Local_Variant(cs, "other",		&tempvar);
2041         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2042         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2043         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2044         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2045         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2046         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2047         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2048         Script_Set_Local_Variant(cs, "which",		&tempvar);
2049         Script_Set_Local_Variant(cs, "attackid",	&tempvar);
2050         Script_Set_Local_Variant(cs, "tag",	        &tempvar);
2051     }
2052 }
2053 
execute_updateentity_script(entity * ent)2054 void execute_updateentity_script(entity *ent)
2055 {
2056     ScriptVariant tempvar;
2057     Script *cs = ent->scripts->update_script;
2058     if(Script_IsInitialized(cs))
2059     {
2060         ScriptVariant_Init(&tempvar);
2061         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2062         tempvar.ptrVal = (VOID *)ent;
2063         Script_Set_Local_Variant(cs, "self", &tempvar);
2064         Script_Execute(cs);
2065         //clear to save variant space
2066         ScriptVariant_Clear(&tempvar);
2067         Script_Set_Local_Variant(cs, "self", &tempvar);
2068     }
2069 }
2070 
execute_think_script(entity * ent)2071 void execute_think_script(entity *ent)
2072 {
2073     ScriptVariant tempvar;
2074     Script *cs = ent->scripts->think_script;
2075     if(Script_IsInitialized(cs))
2076     {
2077         ScriptVariant_Init(&tempvar);
2078         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2079         tempvar.ptrVal = (VOID *)ent;
2080         Script_Set_Local_Variant(cs, "self", &tempvar);
2081         Script_Execute(cs);
2082         //clear to save variant space
2083         ScriptVariant_Clear(&tempvar);
2084         Script_Set_Local_Variant(cs, "self", &tempvar);
2085     }
2086 }
2087 
_execute_didhit_script(Script * cs,entity * ent,entity * other,s_collision_attack * attack,int blocked)2088 static void _execute_didhit_script(Script *cs, entity *ent, entity *other, s_collision_attack *attack, int blocked)
2089 {
2090     ScriptVariant tempvar;
2091     ScriptVariant_Init(&tempvar);
2092 
2093     ScriptVariant_ChangeType(&tempvar, VT_PTR);
2094 
2095     tempvar.ptrVal = (VOID *)ent;
2096     Script_Set_Local_Variant(cs, "self",        &tempvar);
2097 
2098     tempvar.ptrVal = (VOID *)other;
2099     Script_Set_Local_Variant(cs, "damagetaker", &tempvar);
2100 
2101     ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2102 
2103     tempvar.lVal = (LONG)attack->attack_force;
2104     Script_Set_Local_Variant(cs, "damage",      &tempvar);
2105 
2106     tempvar.lVal = (LONG)attack->attack_drop;
2107     Script_Set_Local_Variant(cs, "drop",        &tempvar);
2108 
2109     tempvar.lVal = (LONG)attack->attack_type;
2110     Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2111 
2112     tempvar.lVal = (LONG)attack->no_block;
2113     Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2114 
2115     tempvar.lVal = (LONG)attack->guardcost;
2116     Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2117 
2118     tempvar.lVal = (LONG)attack->jugglecost;
2119     Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2120 
2121     tempvar.lVal = (LONG)attack->pause_add;
2122     Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2123 
2124     tempvar.lVal = (LONG)attack->tag;
2125     Script_Set_Local_Variant(cs, "tag",    &tempvar);
2126 
2127     tempvar.lVal = (LONG)blocked;
2128     Script_Set_Local_Variant(cs, "blocked",    &tempvar);
2129 
2130 
2131     Script_Execute(cs);
2132     //clear to save variant space
2133     ScriptVariant_Clear(&tempvar);
2134     Script_Set_Local_Variant(cs, "self",        &tempvar);
2135     Script_Set_Local_Variant(cs, "damagetaker", &tempvar);
2136     Script_Set_Local_Variant(cs, "damage",      &tempvar);
2137     Script_Set_Local_Variant(cs, "drop",        &tempvar);
2138     Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2139     Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2140     Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2141     Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2142     Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2143     Script_Set_Local_Variant(cs, "blocked",     &tempvar);
2144     Script_Set_Local_Variant(cs, "tag",         &tempvar);
2145 }
2146 
execute_didhit_script(entity * ent,entity * other,s_collision_attack * attack,int blocked)2147 void execute_didhit_script(entity *ent, entity *other, s_collision_attack *attack, int blocked)
2148 {
2149     Script *cs;
2150     s_scripts *gs = global_model_scripts;
2151     if(gs && (cs = gs->didhit_script) && Script_IsInitialized(cs))
2152     {
2153         _execute_didhit_script(cs, ent, other, attack, blocked);
2154     }
2155     if(Script_IsInitialized(cs = ent->scripts->didhit_script))
2156     {
2157         _execute_didhit_script(cs, ent, other, attack, blocked);
2158     }
2159 }
2160 
_execute_onspawn_script(Script * cs,entity * ent)2161 static void _execute_onspawn_script(Script *cs, entity *ent)
2162 {
2163     ScriptVariant tempvar;
2164     ScriptVariant_Init(&tempvar);
2165     ScriptVariant_ChangeType(&tempvar, VT_PTR);
2166     tempvar.ptrVal = (VOID *)ent;
2167     Script_Set_Local_Variant(cs, "self", &tempvar);
2168     Script_Execute(cs);
2169     //clear to save variant space
2170     ScriptVariant_Clear(&tempvar);
2171     Script_Set_Local_Variant(cs, "self", &tempvar);
2172 }
2173 
execute_onspawn_script(entity * ent)2174 void execute_onspawn_script(entity *ent)
2175 {
2176     Script *cs;
2177     s_scripts *gs = global_model_scripts;
2178     if(gs && (cs = gs->onspawn_script) && Script_IsInitialized(cs))
2179     {
2180         _execute_onspawn_script(cs, ent);
2181     }
2182     if(Script_IsInitialized(cs = ent->scripts->onspawn_script))
2183     {
2184         _execute_onspawn_script(cs, ent);
2185     }
2186 }
2187 
execute_onmodelcopy_script(entity * ent,entity * old)2188 void execute_onmodelcopy_script(entity *ent, entity *old)
2189 {
2190     ScriptVariant tempvar;
2191     Script *cs = ent->scripts->onmodelcopy_script;
2192     if(Script_IsInitialized(cs))
2193     {
2194         ScriptVariant_Init(&tempvar);
2195         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2196         tempvar.ptrVal = (VOID *)ent;
2197         Script_Set_Local_Variant(cs, "self", &tempvar);
2198         tempvar.ptrVal = (VOID *)old;
2199         Script_Set_Local_Variant(cs, "old", &tempvar);
2200         Script_Execute(cs);
2201         //clear to save variant space
2202         ScriptVariant_Clear(&tempvar);
2203         Script_Set_Local_Variant(cs, "self", &tempvar);
2204         Script_Set_Local_Variant(cs, "old", &tempvar);
2205     }
2206 }
2207 
execute_ondraw_script(entity * ent)2208 void execute_ondraw_script(entity *ent)
2209 {
2210     ScriptVariant tempvar;
2211     Script *cs = ent->scripts->ondraw_script;
2212     if(Script_IsInitialized(cs))
2213     {
2214         ScriptVariant_Init(&tempvar);
2215         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2216         tempvar.ptrVal = (VOID *)ent;
2217         Script_Set_Local_Variant(cs, "self", &tempvar);
2218         Script_Execute(cs);
2219         //clear to save variant space
2220         ScriptVariant_Clear(&tempvar);
2221         Script_Set_Local_Variant(cs, "self", &tempvar);
2222     }
2223 }
2224 
execute_entity_key_script(entity * ent)2225 void execute_entity_key_script(entity *ent)
2226 {
2227     ScriptVariant tempvar;
2228     Script *cs ;
2229     if(!ent)
2230     {
2231         return;
2232     }
2233     cs = ent->scripts->key_script;
2234     if(Script_IsInitialized(cs))
2235     {
2236         ScriptVariant_Init(&tempvar);
2237         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2238         tempvar.ptrVal = (VOID *)ent;
2239         Script_Set_Local_Variant(cs, "self",    &tempvar);
2240         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2241         tempvar.lVal = (LONG)ent->playerindex;
2242         Script_Set_Local_Variant(cs, "player",  &tempvar);
2243         Script_Execute(cs);
2244         //clear to save variant space
2245         ScriptVariant_Clear(&tempvar);
2246         Script_Set_Local_Variant(cs, "self",    &tempvar);
2247         Script_Set_Local_Variant(cs, "player",  &tempvar);
2248     }
2249 }
2250 
execute_spawn_script(s_spawn_entry * p,entity * e)2251 void execute_spawn_script(s_spawn_entry *p, entity *e)
2252 {
2253     ScriptVariant tempvar;
2254     Script *cs;
2255     cs = &p->spawnscript;
2256     if(Script_IsInitialized(cs))
2257     {
2258         if(e)
2259         {
2260             ScriptVariant_Init(&tempvar);
2261             ScriptVariant_ChangeType(&tempvar, VT_PTR);
2262             tempvar.ptrVal = (VOID *)e;
2263             Script_Set_Local_Variant(cs, "self", &tempvar);
2264             ScriptVariant_ChangeType(&tempvar, VT_DECIMAL);
2265             tempvar.dblVal = (DOUBLE)p->position.x;
2266             Script_Set_Local_Variant(cs, "spawnx", &tempvar);
2267             tempvar.dblVal = (DOUBLE)p->position.z;
2268             Script_Set_Local_Variant(cs, "spawnz", &tempvar);
2269             tempvar.dblVal = (DOUBLE)p->position.y;
2270             Script_Set_Local_Variant(cs, "spawna", &tempvar);
2271             ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2272             tempvar.lVal = (LONG)p->at;
2273             Script_Set_Local_Variant(cs, "spawnat", &tempvar);
2274         }
2275         Script_Execute(cs);
2276         if(e)
2277         {
2278             ScriptVariant_Clear(&tempvar);
2279             Script_Set_Local_Variant(cs, "self", &tempvar);
2280             Script_Set_Local_Variant(cs, "spawnx", &tempvar);
2281             Script_Set_Local_Variant(cs, "spawnz", &tempvar);
2282             Script_Set_Local_Variant(cs, "spawna", &tempvar);
2283             Script_Set_Local_Variant(cs, "spawnat", &tempvar);
2284         }
2285     }
2286 }
2287 
execute_level_key_script(int player)2288 void execute_level_key_script(int player)
2289 {
2290     ScriptVariant tempvar;
2291     Script *cs = &(level->key_script);
2292     if(Script_IsInitialized(cs))
2293     {
2294         ScriptVariant_Init(&tempvar);
2295         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2296         tempvar.lVal = (LONG)player;
2297         Script_Set_Local_Variant(cs, "player", &tempvar);
2298         Script_Execute(cs);
2299         //clear to save variant space
2300         ScriptVariant_Clear(&tempvar);
2301         Script_Set_Local_Variant(cs, "player", &tempvar);
2302     }
2303 }
2304 
execute_input_script_all(int player)2305 void execute_input_script_all(int player)
2306 {
2307 	ScriptVariant tempvar;
2308 	Script *cs = &input_script_all;
2309 	if (Script_IsInitialized(cs))
2310 	{
2311 		ScriptVariant_Init(&tempvar);
2312 
2313 		//ScriptVariant_ChangeType(&tempvar, VT_PTR);
2314 
2315 		//tempvar.ptrVal = (VOID *)player_object;
2316 		//Script_Set_Local_Variant(cs, "player_object", &tempvar);
2317 
2318 		ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2319 		tempvar.lVal = (LONG)player;
2320 
2321 		Script_Set_Local_Variant(cs, "player", &tempvar);
2322 
2323 		Script_Execute(cs);
2324 
2325 		//clear to save variant space
2326 		ScriptVariant_Clear(&tempvar);
2327 		Script_Set_Local_Variant(cs, "player", &tempvar);
2328 		//Script_Set_Local_Variant(cs, "player_object", &tempvar);
2329 	}
2330 }
2331 
2332 
execute_key_script_all(int player)2333 void execute_key_script_all(int player)
2334 {
2335     ScriptVariant tempvar;
2336     Script *cs = &key_script_all;
2337     if(Script_IsInitialized(cs))
2338     {
2339         ScriptVariant_Init(&tempvar);
2340         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2341         tempvar.lVal = (LONG)player;
2342         Script_Set_Local_Variant(cs, "player", &tempvar);
2343         Script_Execute(cs);
2344         //clear to save variant space
2345         ScriptVariant_Clear(&tempvar);
2346         Script_Set_Local_Variant(cs, "player", &tempvar);
2347     }
2348 }
2349 
execute_timetick_script(int time,int gotime)2350 void execute_timetick_script(int time, int gotime)
2351 {
2352     ScriptVariant tempvar;
2353     Script *cs = &timetick_script;
2354     if(Script_IsInitialized(cs))
2355     {
2356         ScriptVariant_Init(&tempvar);
2357         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2358         tempvar.lVal = (LONG)time;
2359         Script_Set_Local_Variant(cs, "time",    &tempvar);
2360         tempvar.lVal = (LONG)gotime;
2361         Script_Set_Local_Variant(cs, "gotime", &tempvar);
2362         Script_Execute(cs);
2363         //clear to save variant space
2364         ScriptVariant_Clear(&tempvar);
2365         Script_Set_Local_Variant(cs, "time",    &tempvar);
2366         Script_Set_Local_Variant(cs, "gotime",  &tempvar);
2367     }
2368 }
2369 
execute_loading_script(int value,int max)2370 void execute_loading_script(int value, int max)
2371 {
2372     ScriptVariant tempvar;
2373     Script *cs = &loading_script;
2374     if(Script_IsInitialized(cs))
2375     {
2376         ScriptVariant_Init(&tempvar);
2377         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2378         tempvar.lVal = (LONG)value;
2379         Script_Set_Local_Variant(cs, "value",    &tempvar);
2380         tempvar.lVal = (LONG)max;
2381         Script_Set_Local_Variant(cs, "max", &tempvar);
2382         Script_Execute(cs);
2383         //clear to save variant space
2384         ScriptVariant_Clear(&tempvar);
2385         Script_Set_Local_Variant(cs, "value",    &tempvar);
2386         Script_Set_Local_Variant(cs, "max",  &tempvar);
2387     }
2388 }
2389 
execute_key_script(int index)2390 void execute_key_script(int index)
2391 {
2392     if(Script_IsInitialized(&key_script[index]))
2393     {
2394         Script_Execute(&key_script[index]);
2395     }
2396 }
2397 
execute_join_script(int index)2398 void execute_join_script(int index)
2399 {
2400     if(Script_IsInitialized(&join_script[index]))
2401     {
2402         Script_Execute(&join_script[index]);
2403     }
2404 }
2405 
execute_respawn_script(int index)2406 void execute_respawn_script(int index)
2407 {
2408     if(Script_IsInitialized(&respawn_script[index]))
2409     {
2410         Script_Execute(&respawn_script[index]);
2411     }
2412 }
2413 
execute_pdie_script(int index)2414 void execute_pdie_script(int index)
2415 {
2416     if(Script_IsInitialized(&pdie_script[index]))
2417     {
2418         Script_Execute(&pdie_script[index]);
2419     }
2420 }
2421 
2422 // ------------------------ Save/load -----------------------------
2423 
clearsettings()2424 void clearsettings()
2425 {
2426     int i = 0;
2427 
2428     savedata.compatibleversion = COMPATIBLEVERSION;
2429     savedata.gamma = 0;
2430     savedata.brightness = 0;
2431     savedata.soundvol = 15;
2432     savedata.usemusic = 1;
2433     savedata.musicvol = 100;
2434     savedata.effectvol = 120;
2435     savedata.usejoy = 1;
2436     savedata.mode = 0;
2437     savedata.showtitles = 0;
2438     savedata.windowpos = 0;
2439     savedata.logo = 0;
2440     savedata.uselog = 1;
2441     savedata.debuginfo = 0;
2442     savedata.fullscreen = 0;
2443     savedata.vsync = 1;
2444 
2445 	#if WII
2446     savedata.stretch = 1;
2447 	#else
2448     savedata.stretch = 0;
2449 	#endif
2450 
2451     savedata.swfilter = 0;
2452 
2453     #ifdef SDL
2454     savedata.usegl = 1;
2455     savedata.hwfilter = 1;
2456         #ifdef ANDROID
2457         savedata.hwscale = 0.0;
2458         #else
2459         savedata.hwscale = 1.0;
2460         #endif
2461     #endif
2462 
2463     #ifdef PSP
2464     savedata.pspcpuspeed = 2;
2465     savedata.overscan[0] = 0;
2466     savedata.overscan[1] = 0;
2467     savedata.overscan[2] = 0;
2468     savedata.overscan[3] = 0;
2469     #endif
2470 
2471     #ifdef ANDROID
2472     savedata.is_touchpad_vibration_enabled = 0;
2473     #endif
2474 
2475     for (i = 0; i < MAX_PLAYERS; i++)
2476     {
2477         savedata.joyrumble[i] = 0;
2478     }
2479 
2480     control_clearmappings();
2481 }
2482 
2483 
savesettings()2484 void savesettings()
2485 {
2486 #ifndef DC
2487     FILE *handle = NULL;
2488     char path[MAX_BUFFER_LEN] = {""};
2489     char tmpname[MAX_BUFFER_LEN] = {""};
2490     getBasePath(path, "Saves", 0);
2491     getPakName(tmpname, 4);
2492     strcat(path, tmpname);
2493     handle = fopen(path, "wb");
2494     if(handle == NULL)
2495     {
2496         return;
2497     }
2498     fwrite(&savedata, 1, sizeof(savedata), handle);
2499     fclose(handle);
2500 
2501     // save controls
2502     getBasePath(path, "Saves", 0);
2503     getPakName(tmpname, 5);
2504     strcat(path, tmpname);
2505     if (!control_savemappings(path))
2506     {
2507         printf("Failed to save controls to %s\n", path);
2508     }
2509 #endif
2510 }
2511 
saveasdefault()2512 void saveasdefault()
2513 {
2514 #ifndef DC
2515     FILE *handle = NULL;
2516     char path[MAX_BUFFER_LEN] = {""};
2517     getBasePath(path, "Saves", 0);
2518     strcat(path, "default.cfg");
2519     handle = fopen(path, "wb");
2520     if(handle == NULL)
2521     {
2522         return;
2523     }
2524     fwrite(&savedata, 1, sizeof(savedata), handle);
2525     fclose(handle);
2526 
2527     // save controls
2528     getBasePath(path, "Saves", 0);
2529     strcat(path, "default.controls");
2530     if (!control_savemappings(path))
2531     {
2532         printf("Failed to save controls to %s\n", path);
2533     }
2534 #endif
2535 }
2536 
2537 
loadsettings()2538 void loadsettings()
2539 {
2540 #ifndef DC
2541     FILE *handle = NULL;
2542     char path[MAX_BUFFER_LEN] = {""};
2543     char tmpname[MAX_BUFFER_LEN] = {""};
2544     getBasePath(path, "Saves", 0);
2545     getPakName(tmpname, 4);
2546     strcat(path, tmpname);
2547     if(!(fileExists(path)))
2548     {
2549         loadfromdefault();
2550         return;
2551     }
2552     clearsettings();
2553     handle = fopen(path, "rb");
2554     if(handle == NULL)
2555     {
2556         return;
2557     }
2558     fread(&savedata, 1, sizeof(savedata), handle);
2559     fclose(handle);
2560     if(savedata.compatibleversion != COMPATIBLEVERSION)
2561     {
2562         clearsettings();
2563     }
2564 
2565     // load controls
2566     getBasePath(path, "Saves", 0);
2567     getPakName(tmpname, 5);
2568     strcat(path, tmpname);
2569     if (!control_loadmappings(path))
2570     {
2571         printf("Failed to load controls from %s\n", path);
2572     }
2573 #else
2574     clearsettings();
2575 #endif
2576 }
2577 
loadfromdefault()2578 void loadfromdefault()
2579 {
2580 #ifndef DC
2581     FILE *handle = NULL;
2582     char path[MAX_BUFFER_LEN] = {""};
2583     getBasePath(path, "Saves", 0);
2584     strcat(path, "default.cfg");
2585     clearsettings();
2586     handle = fopen(path, "rb");
2587     if(handle == NULL)
2588     {
2589         return;
2590     }
2591     fread(&savedata, 1, sizeof(savedata), handle);
2592     fclose(handle);
2593     if(savedata.compatibleversion != COMPATIBLEVERSION)
2594     {
2595         clearsettings();
2596     }
2597 
2598     // load controls
2599     getBasePath(path, "Saves", 0);
2600     strcat(path, "default.controls");
2601     if (!control_loadmappings(path))
2602     {
2603         printf("Failed to load controls from %s\n", path);
2604     }
2605 #else
2606     clearsettings();
2607 #endif
2608 }
2609 
2610 
2611 
2612 
clearSavedGame()2613 void clearSavedGame()
2614 {
2615     memset(savelevel, 0, sizeof(*savelevel)*num_difficulties);
2616 }
2617 
2618 
2619 
clearHighScore()2620 void clearHighScore()
2621 {
2622     int i;
2623     savescore.compatibleversion = CV_HIGH_SCORE;
2624     for(i = 0; i < 10; i++)
2625     {
2626         savescore.highsc[i] = 0;    // Resets all the highscores to 0
2627         strcpy(savescore.hscoren[i], "None");    // Resets all the highscore names to "None"
2628     }
2629 }
2630 
2631 
2632 
saveGameFile()2633 int saveGameFile()
2634 {
2635 #ifndef DC
2636     FILE *handle = NULL;
2637     char path[MAX_BUFFER_LEN] = {""};
2638     char tmpname[MAX_BUFFER_LEN] = {""};
2639 
2640     getBasePath(path, "Saves", 0);
2641     getPakName(tmpname, 0);
2642     strcat(path, tmpname);
2643     //if(!savelevel[saveslot].level) return;
2644     handle = fopen(path, "wb");
2645 
2646     if(handle == NULL)
2647     {
2648         return 0;
2649     }
2650 
2651     fwrite(savelevel, sizeof(*savelevel), num_difficulties, handle);
2652 
2653     fclose(handle);
2654 
2655     return 1;
2656 #else
2657     return 1;
2658 #endif
2659 }
2660 
2661 
loadGameFile()2662 int loadGameFile()
2663 {
2664 #ifndef DC
2665     int result = 1, i;
2666     FILE *handle = NULL;
2667     char path[MAX_BUFFER_LEN] = {""};
2668     char tmpname[MAX_BUFFER_LEN] = {""};
2669     //size_t filesize = 0;
2670 
2671     getBasePath(path, "Saves", 0);
2672     getPakName(tmpname, 0);
2673     strcat(path, tmpname);
2674     handle = fopen(path, "rb");
2675 
2676     if(handle == NULL)
2677     {
2678         return 0;
2679     }
2680 
2681     //fseek(handle, 0L, SEEK_END);
2682     //filesize = ftell(handle);
2683     //fseek(handle, 0L, SEEK_SET); // or rewind(handle);
2684     //(filesize != sizeof(*savelevel)*num_difficulties)
2685 
2686     if( (fread(savelevel, sizeof(*savelevel), num_difficulties, handle) >= sizeof(*savelevel) && savelevel[0].compatibleversion != CV_SAVED_GAME) )
2687     {
2688         clearSavedGame();
2689         result = 0;
2690     }
2691     else
2692     {
2693         bonus = 0;
2694         for(i = 0; i < num_difficulties; i++) if(savelevel[i].times_completed > 0)
2695             {
2696                 bonus += savelevel[i].times_completed;
2697             }
2698     }
2699 
2700     fclose(handle);
2701 
2702     return result;
2703 #else
2704     clearSavedGame();
2705     return 0;
2706 #endif
2707 }
2708 
2709 
saveHighScoreFile()2710 int saveHighScoreFile()
2711 {
2712 #ifndef DC
2713     FILE *handle = NULL;
2714     char path[MAX_BUFFER_LEN] = {""};
2715     char tmpname[MAX_BUFFER_LEN] = {""};
2716     getBasePath(path, "Saves", 0);
2717     getPakName(tmpname, 1);
2718     strcat(path, tmpname);
2719     handle = fopen(path, "wb");
2720     if(handle == NULL)
2721     {
2722         return 0;
2723     }
2724     fwrite(&savescore, 1, sizeof(savescore), handle);
2725     fclose(handle);
2726     return 1;
2727 #else
2728     return 1;
2729 #endif
2730 }
2731 
2732 
loadHighScoreFile()2733 int loadHighScoreFile()
2734 {
2735 #ifndef DC
2736     FILE *handle = NULL;
2737     char path[MAX_BUFFER_LEN] = {""};
2738     char tmpname[MAX_BUFFER_LEN] = {""};
2739     getBasePath(path, "Saves", 0);
2740     getPakName(tmpname, 1);
2741     strcat(path, tmpname);
2742     clearHighScore();
2743     handle = fopen(path, "rb");
2744     if(handle == NULL)
2745     {
2746         return 0;
2747     }
2748     fread(&savescore, 1, sizeof(savescore), handle);
2749     fclose(handle);
2750     if(savescore.compatibleversion != CV_HIGH_SCORE)
2751     {
2752         clearHighScore();
2753         return 0;
2754     }
2755     return 1;
2756 #else
2757     clearHighScore();
2758     return 0;
2759 #endif
2760 }
2761 
2762 
2763 #ifndef DC
vardump(ScriptVariant * var,char buffer[])2764 static void vardump(ScriptVariant *var, char buffer[])
2765 {
2766     char *tmpstr;
2767     int l, t, c;
2768     buffer[0] = 0;
2769     switch(var->vt)
2770     {
2771 
2772     case VT_STR:
2773         strcpy(buffer, "\"");
2774         tmpstr = StrCache_Get(var->strVal);
2775         l = strlen(tmpstr);
2776         for(c = 0; c < l; c++)
2777         {
2778             if(tmpstr[c] == '\n')
2779             {
2780                 strcat(buffer, "\\n");
2781             }
2782             else if(tmpstr[c] == '\r')
2783             {
2784                 strcat(buffer, "\\r");
2785             }
2786             else if(tmpstr[c] == '\\')
2787             {
2788                 strcat(buffer, "\\\\");
2789             }
2790             else
2791             {
2792                 t = strlen(buffer);
2793                 buffer[t] = tmpstr[c];
2794                 buffer[t + 1] = 0;
2795             }
2796         }
2797         strcat(buffer, "\"");
2798         break;
2799     case VT_DECIMAL:
2800         sprintf(buffer, "%lf", (double)var->dblVal);
2801         break;
2802     case VT_INTEGER:
2803         sprintf(buffer, "%ld", (long)var->lVal);
2804         break;
2805     default:
2806         strcpy(buffer, "NULL()");
2807         break;
2808     }
2809 }
2810 
2811 #endif
2812 
2813 
saveScriptFile()2814 int saveScriptFile()
2815 {
2816 #ifndef DC
2817 #define _writestr(v) fwrite(v, strlen(v), 1, handle);
2818 #define _writetmp  _writestr(tmpvalue)
2819 #define _writeconst(s) strcpy(tmpvalue,s);_writetmp
2820     FILE *handle = NULL;
2821     int i, l, size;
2822     ScriptVariant *var;
2823     char path[MAX_BUFFER_LEN] = {""};
2824     char tmpvalue[MAX_BUFFER_LEN] = {""};
2825     getBasePath(path, "Saves", 0);
2826     getPakName(tmpvalue, 2);//.scr
2827     strcat(path, tmpvalue);
2828     l = strlen(path); //s00, s01, s02 etc
2829     path[l - 2] = '0' + (current_set / 10);
2830     path[l - 1] = '0' + (current_set % 10);
2831     handle = fopen(path, "wb");
2832     if(handle == NULL)
2833     {
2834         return 0;
2835     }
2836 
2837     _writeconst("void main() {\n");
2838     size = List_GetSize(global_var_list.list);
2839     for(i = 0, List_Reset(global_var_list.list); i < size; List_GotoNext(global_var_list.list), i++)
2840     {
2841         var = (ScriptVariant *)List_Retrieve(global_var_list.list);
2842         if( var->vt != VT_EMPTY && var->vt != VT_PTR)
2843         {
2844             _writeconst("\tsetglobalvar(\"")
2845             _writestr(List_GetName(global_var_list.list))
2846             _writeconst("\",")
2847             vardump(var, tmpvalue);
2848             _writetmp
2849             _writeconst(");\n")
2850         }
2851     }
2852     // indexed list
2853     for(i = 1; i <= global_var_list.vars->lVal; i++)
2854     {
2855         if(global_var_list.vars[i].vt != VT_PTR && global_var_list.vars[i].vt != VT_EMPTY)
2856         {
2857             _writeconst("\tsetglobalvar(")
2858             sprintf(tmpvalue, "%d", i - 1);
2859             _writetmp
2860             _writeconst(",")
2861             vardump(global_var_list.vars + i, tmpvalue);
2862             _writetmp
2863             _writeconst(");\n")
2864         }
2865     }
2866     //allow select
2867     for(i = 0; i < models_cached; i++)
2868     {
2869         if(model_cache[i].selectable)
2870         {
2871             _writeconst("\tchangemodelproperty(\"")
2872             _writestr(model_cache[i].name)
2873             _writeconst("\",4,1);\n")
2874         }
2875         /*
2876         if(model_cache[i].model) {
2877         	_writeconst("\tloadmodel(\"")
2878         	_writestr(model_cache[i].name)
2879         	sprintf(tmpvalue, "\",%d,%d);\n", model_cache[i].model->unload, model_cache[i].selectable);
2880         	_writetmp
2881         }*/
2882     }
2883     _writeconst("}\n");
2884 
2885     fclose(handle);
2886     return 1;
2887 #undef _writestr
2888 #undef _writetmp
2889 #undef _writeconst
2890 #else
2891     return 1;
2892 #endif
2893 }
2894 
2895 
loadScriptFile()2896 int loadScriptFile()
2897 {
2898 #ifndef DC
2899     Script script;
2900     int result = 0;
2901     char *buf = NULL;
2902     ptrdiff_t l;
2903     size_t len;
2904     FILE *handle = NULL;
2905 
2906     char path[MAX_BUFFER_LEN] = {""};
2907     char tmpname[MAX_BUFFER_LEN] = {""};
2908     getBasePath(path, "Saves", 0);
2909     getPakName(tmpname, 2);//.scr
2910     strcat(path, tmpname);
2911     l = strlen(path); //s00, s01, s02 etc
2912     path[l - 2] = '0' + (current_set / 10);
2913     path[l - 1] = '0' + (current_set % 10);
2914 
2915     handle = fopen(path, "rb");
2916     if(handle == NULL)
2917     {
2918         return 0;
2919     }
2920 
2921     fseek(handle, 0, SEEK_END);
2922     len = ftell(handle);
2923     fseek(handle, 0, SEEK_SET);
2924     buf = malloc(len + 1);
2925 
2926     if(!buf)
2927     {
2928         return 0;
2929     }
2930 
2931     fread(buf, 1, len, handle);
2932     buf[len - 1] = 0;
2933 
2934     Script_Init(&script, "loadScriptFile",  NULL, 1);
2935 
2936     result = (Script_AppendText(&script, buf, path) &&
2937               Script_Compile(&script) &&
2938               Script_Execute(&script) );
2939 
2940     Script_Clear(&script, 2);
2941     free(buf);
2942     return result;
2943 #else
2944     return 0;
2945 #endif
2946 }
2947 
2948 // ----------------------- Sound ------------------------------
2949 
music(char * filename,int loop,long offset)2950 int music(char *filename, int loop, long offset)
2951 {
2952     char t[64];
2953     char a[64];
2954     int res = 1;
2955 
2956     if(!savedata.usemusic)
2957     {
2958         return 0;
2959     }
2960     if(!sound_open_music(filename, packfile, savedata.musicvol, loop, offset))
2961     {
2962         printf("\nCan't play music file '%s'\n", filename);
2963         res = 0;
2964     }
2965     if(savedata.showtitles && sound_query_music(a, t))
2966     {
2967         debug_xy_msg.font_index = 0;
2968         //debug_xy_msg.x = videomodes.hRes/2 - videomodes.hShift - (fontmonowidth(debug_xy_msg.font_index)*16);
2969         //debug_xy_msg.y = videomodes.vRes - videomodes.vShift - fontheight(debug_xy_msg.font_index);
2970         debug_xy_msg.x = fontmonowidth(debug_xy_msg.font_index);
2971         debug_xy_msg.y = videomodes.vRes - fontheight(debug_xy_msg.font_index)*2;
2972         if(a[0] && t[0])
2973         {
2974             debug_printf("Playing \"%s\" by %s", t, a);
2975         }
2976         else if(a[0])
2977         {
2978             debug_printf("Playing unknown song by %s", a);
2979         }
2980         else if(t[0])
2981         {
2982             debug_printf("Playing \"%s\" by unknown artist", t);
2983         }
2984         else
2985         {
2986             debug_printf("");
2987         }
2988     }
2989     strncpy(currentmusic, filename, sizeof(currentmusic) - 1);
2990     return res;
2991 }
2992 
check_music()2993 void check_music()
2994 {
2995     if(musicfade[1] > 0)
2996     {
2997         musicfade[1] -= musicfade[0];
2998         sound_volume_music((int)musicfade[1], (int)musicfade[1]);
2999     }
3000     else if(musicname[0])
3001     {
3002         music(musicname, musicloop, musicoffset);
3003         sound_volume_music(savedata.musicvol, savedata.musicvol);
3004         musicname[0] = 0;
3005     }
3006 }
3007 
3008 // ----------------------- General ------------------------------
3009 // atof and atoi return a valid number, if only the first char is one.
3010 // so we only check that.
isNumeric(char * text)3011 int isNumeric(char *text)
3012 {
3013     char *p = text;
3014     assert(p);
3015     if(!*p)
3016     {
3017         return 0;
3018     }
3019     switch(*p)
3020     {
3021     case '-':
3022     case '+':
3023         p++;
3024         break;
3025     default:
3026         break;
3027     }
3028     switch (*p)
3029     {
3030     case '0':
3031     case '1':
3032     case '2':
3033     case '3':
3034     case '4':
3035     case '5':
3036     case '6':
3037     case '7':
3038     case '8':
3039     case '9':
3040         return 1;
3041     default:
3042         return 0;
3043     }
3044     return 1;
3045 }
3046 
3047 
getValidInt(char * text,char * file,char * cmd)3048 int getValidInt(char *text, char *file, char *cmd)
3049 {
3050     static const char *WARN_NUMBER_EXPECTED = "WARNING: %s tries to load a non-numeric value at %s, where a number is expected!\nerroneus string: %s\n";
3051     if(!text || !*text)
3052     {
3053         return 0;
3054     }
3055     if(isNumeric(text))
3056     {
3057         return atoi(text);
3058     }
3059     else
3060     {
3061         printf(WARN_NUMBER_EXPECTED, file, cmd, text);
3062         return 0;
3063     }
3064 
3065 }
3066 
getValidFloat(char * text,char * file,char * cmd)3067 float getValidFloat(char *text, char *file, char *cmd)
3068 {
3069     static const char *WARN_NUMBER_EXPECTED = "WARNING: %s tries to load a non-numeric value at %s, where a number is expected!\nerroneus string: %s\n";
3070     if(!text || !*text)
3071     {
3072         return 0.0f;
3073     }
3074     if(isNumeric(text))
3075     {
3076         if(text[strlen(text) - 1] == '%')
3077         {
3078             return atof(text) / 100.0f;
3079         }
3080         return atof(text);
3081     }
3082     else
3083     {
3084         printf(WARN_NUMBER_EXPECTED, file, cmd, text);
3085         return 0.0f;
3086     }
3087 }
3088 
ParseArgs(ArgList * list,char * input,char * output)3089 size_t ParseArgs(ArgList *list, char *input, char *output)
3090 {
3091     assert(list);
3092     assert(input);
3093     assert(output);
3094 
3095     memset(output,0,MAX_ARG_LEN);
3096 
3097     size_t pos = 0;
3098     size_t wordstart = 0;
3099     size_t item = 0;
3100     // flags
3101     int done_flag = 0;
3102     int space_flag = 0; // can find more spaces
3103     int double_apex_flag = 0;
3104     int single_apex_flag = 0;
3105 
3106     while(pos < MAX_ARG_LEN - 1 && item < MAX_ARG_COUNT)
3107     {
3108         switch(input[pos])
3109         {
3110             // read strings
3111             case '"':
3112                 if ( (pos > 0 && input[pos-1] != '\\') || pos <= 0 )
3113                 {
3114                     if (space_flag && !double_apex_flag)
3115                     {
3116                         double_apex_flag = 1;
3117                         space_flag = 0;
3118                         wordstart = pos;
3119                         output[pos] = input[pos];
3120                         break;
3121                     }
3122                     else if (double_apex_flag)
3123                     {
3124                         double_apex_flag = 0;
3125                         output[pos] = input[pos];
3126                         // continue to get inputs
3127                         break;
3128                     }
3129                     else
3130                     {
3131                         if(space_flag)
3132                         {
3133                             wordstart = pos;
3134                         }
3135                         output[pos] = input[pos];
3136                         space_flag = 0;
3137                         break;
3138                     }
3139                 }
3140                 else
3141                 {
3142                     if(space_flag)
3143                     {
3144                         wordstart = pos;
3145                     }
3146                     output[pos] = input[pos];
3147                     space_flag = 0;
3148                     break;
3149                 }
3150             case '\'':
3151                 if ( (pos > 0 && input[pos-1] != '\\') || pos <= 0 )
3152                 {
3153                     if (space_flag && !single_apex_flag)
3154                     {
3155                         single_apex_flag = 1;
3156                         space_flag = 0;
3157                         wordstart = pos;
3158                         output[pos] = input[pos];
3159                         break;
3160                     }
3161                     else if (single_apex_flag)
3162                     {
3163                         single_apex_flag = 0;
3164                         output[pos] = input[pos];
3165                         // continue to get inputs
3166                         break;
3167                     }
3168                     else
3169                     {
3170                         if(space_flag)
3171                         {
3172                             wordstart = pos;
3173                         }
3174                         output[pos] = input[pos];
3175                         space_flag = 0;
3176                         break;
3177                     }
3178                 }
3179                 else
3180                 {
3181                     if(space_flag)
3182                     {
3183                         wordstart = pos;
3184                     }
3185                     output[pos] = input[pos];
3186                     space_flag = 0;
3187                     break;
3188                 }
3189 
3190             // complete item
3191             case '\r':
3192             case '\n':
3193             case '#':
3194                 if (double_apex_flag || single_apex_flag)
3195                 {
3196                     output[pos] = input[pos];
3197                     break;
3198                 }
3199             case '\0':
3200                 done_flag = 1;
3201 
3202             // skip spaces
3203             case ' ':
3204             case '\t':
3205                 if (!double_apex_flag && !single_apex_flag)
3206                 {
3207                     output[pos] = '\0';
3208                     if(!space_flag && wordstart != pos)
3209                     {
3210                         list->args[item] = output + wordstart;
3211                         list->arglen[item] = pos - wordstart;
3212                         item++;
3213                     }
3214                     space_flag = 1;
3215                     break;
3216                 }
3217 
3218             // read character
3219             default:
3220                 if(space_flag && !double_apex_flag && !single_apex_flag)
3221                 {
3222                     wordstart = pos;
3223                 }
3224                 output[pos] = input[pos];
3225                 space_flag = 0;
3226         }
3227 
3228         if(done_flag)
3229         {
3230             break;
3231         }
3232         pos++;
3233     }
3234     list->count = item;
3235 
3236     // TEST
3237     /*printf("found: ");
3238     int i;
3239     for (i = 0; i < list->count; i++) {
3240         printf("|%s|:%d",list->args[i],list->arglen[i]);
3241         if (i < list->count - 1) printf(" ");
3242     }
3243     printf("\n");*/
3244 
3245     return item;
3246 }
3247 
readByte(char * buf)3248 int readByte(char *buf)
3249 {
3250     int num = 0;
3251 
3252     num = (unsigned int)buf[0]&0xFF;
3253 
3254     return num;
3255 }
3256 
findarg(char * command,int which)3257 char *findarg(char *command, int which)
3258 {
3259     static const char comment_mark[] = {"#"};
3260     int d;
3261     int argc;
3262     int inarg;
3263     int argstart;
3264     static char arg[MAX_ARG_LEN];
3265 
3266 
3267     // Copy the command line, replacing spaces by zeroes,
3268     // finally returning a pointer to the requested arg.
3269     d = 0;
3270     inarg = 0;
3271     argstart = 0;
3272     argc = -1;
3273 
3274     while(d < MAX_ARG_LEN - 1 && command[d])
3275     {
3276         // Zero out whitespace
3277         if(command[d] == ' ' || command[d] == '\t')
3278         {
3279             arg[d] = 0;
3280             inarg = 0;
3281             if(argc == which)
3282             {
3283                 return arg + argstart;
3284             }
3285         }
3286         else if(command[d] == 0 || command[d] == '\n' || command[d] == '\r' ||
3287                 strcmp(command + d, comment_mark) == 0)
3288         {
3289             // End of line
3290             arg[d] = 0;
3291             if(argc == which)
3292             {
3293                 return arg + argstart;
3294             }
3295             return arg + d;
3296         }
3297         else
3298         {
3299             if(!inarg)
3300             {
3301                 // if(argc==-1 && command[d]=='#') return arg;
3302                 inarg = 1;
3303                 argstart = d;
3304                 argc++;
3305             }
3306             arg[d] = command[d];
3307         }
3308         ++d;
3309     }
3310     arg[d] = 0;
3311 
3312     return arg;
3313 }
3314 
3315 
3316 
3317 
diff(float a,float b)3318 float diff(float a, float b)
3319 {
3320     if(a < b)
3321     {
3322         return b - a;
3323     }
3324     return a - b;
3325 }
3326 
3327 
3328 
inair(entity * e)3329 int inair(entity *e)
3330 {
3331     return (diff(e->position.y, e->base) >= 0.1);
3332 }
3333 
inair_range(entity * e)3334 int inair_range(entity *e)
3335 {
3336     return (diff(e->position.y, e->base) > T_WALKOFF);
3337 }
3338 
3339 
3340 // ----------------------- Loaders ------------------------------
3341 
3342 
3343 // Creates a remapping table from two images
load_colourmap(s_model * model,char * image1,char * image2)3344 int load_colourmap(s_model *model, char *image1, char *image2)
3345 {
3346     int i, j, k;
3347     unsigned char *map = NULL;
3348     s_bitmap *bitmap1 = NULL;
3349     s_bitmap *bitmap2 = NULL;
3350 
3351     // Can't use same image twice!
3352     if(stricmp(image1, image2) == 0)
3353     {
3354         return 0;
3355     }
3356 
3357     __realloc(model->colourmap, model->maps_loaded);
3358     k = model->maps_loaded++;
3359 
3360     if((map = malloc(MAX_PAL_SIZE / 4)) == NULL)
3361     {
3362         return -2;
3363     }
3364     if((bitmap1 = loadbitmap(image1, packfile, PIXEL_8)) == NULL)
3365     {
3366         free(map);
3367         map = NULL;
3368         return -3;
3369     }
3370     if((bitmap2 = loadbitmap(image2, packfile, PIXEL_8)) == NULL)
3371     {
3372         freebitmap(bitmap1);
3373         free(map);
3374         map = NULL;
3375         return -4;
3376     }
3377 
3378     // Create the colour map
3379     for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3380     {
3381         map[i] = i;
3382     }
3383     for(j = 0; j < bitmap1->height && j < bitmap2->height; j++)
3384     {
3385         for(i = 0; i < bitmap1->width && i < bitmap2->width; i++)
3386         {
3387             map[(unsigned)(bitmap1->data[j * bitmap1->width + i])] = bitmap2->data[j * bitmap2->width + i];
3388         }
3389     }
3390 
3391     freebitmap(bitmap1);
3392     freebitmap(bitmap2);
3393 
3394     model->colourmap[k] = map;
3395     return 1;
3396 }
3397 
3398 //PIXEL_x8
3399 // This function is used to enable remap command in 24bit mode
3400 // So old mods can still run under 16/24/32bit color system
3401 // This function should be called when all colourmaps are loaded, e.g.,
3402 // at the end of load_cached_model
3403 // map flag is used to determine whether a colourmap is a real colourmap
convert_map_to_palette(s_model * model,unsigned mapflag[])3404 int convert_map_to_palette(s_model *model, unsigned mapflag[])
3405 {
3406     int i, c;
3407     unsigned char *newmap, *oldmap;
3408     unsigned char *p1, *p2;
3409     unsigned pb = pixelbytes[(int)PIXEL_32];
3410     if(model->palette == NULL)
3411     {
3412         return 0;
3413     }
3414     for(c = 0; c < model->maps_loaded; c++)
3415     {
3416         if(mapflag[c] == 0)
3417         {
3418             continue;
3419         }
3420         if((newmap = malloc(PAL_BYTES)) == NULL)
3421         {
3422             borShutdown(1, "Error convert_map_to_palette for model: %s\n", model->name);
3423         }
3424         // Create new colour map
3425         memcpy(newmap, model->palette, PAL_BYTES);
3426         oldmap = model->colourmap[c];
3427         for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3428         {
3429             if(oldmap[i] == i)
3430             {
3431                 continue;
3432             }
3433             p1 = newmap + i * pb;
3434             p2 = model->palette + oldmap[i] * pb;
3435             memcpy(p1, p2, pb);
3436         }
3437         model->colourmap[c] = newmap;
3438         free(oldmap);
3439         oldmap = NULL;
3440     }
3441     return 1;
3442 }
3443 
3444 //load a 256 colors' palette
load_palette(unsigned char * palette,char * filename)3445 int load_palette(unsigned char *palette, char *filename)
3446 {
3447     char *fileext;
3448     int handle, i;
3449     unsigned *dp;
3450     unsigned char tpal[3];
3451 
3452     // Determine whether the author is using an .act or image file, and
3453     // verify the file content is valid to load a color table from.
3454     fileext = strrchr(filename, '.');
3455     if(fileext != NULL && stricmp(fileext, ".act") == 0)
3456     {
3457         handle = openpackfile(filename, packfile);
3458         if(handle < 0)
3459         {
3460             return 0;
3461         }
3462         memset(palette, 0, MAX_PAL_SIZE);
3463         dp = (unsigned *)palette;
3464         for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3465         {
3466             if(readpackfile(handle, tpal, 3) != 3)
3467             {
3468                 closepackfile(handle);
3469                 return 0;
3470             }
3471             dp[i] = colour32(tpal[0], tpal[1], tpal[2]);
3472 
3473         }
3474         closepackfile(handle);
3475         dp[0] = 0;
3476 
3477         return 1;
3478     }
3479     else
3480     {
3481         return loadimagepalette(filename, packfile, palette);
3482     }
3483 }
3484 
create_blend_tables_x8(unsigned char * tables[])3485 void create_blend_tables_x8(unsigned char *tables[])
3486 {
3487     int i;
3488     for(i = 0; i < MAX_BLENDINGS; i++)
3489     {
3490         tables[i] = blending_table_functions32[i] ? (blending_table_functions32[i])() : NULL;
3491     }
3492 
3493 }
3494 
3495 
3496 //change system palette by index
change_system_palette(int palindex)3497 void change_system_palette(int palindex)
3498 {
3499     if(palindex < 0)
3500     {
3501         palindex = 0;
3502     }
3503     //if(current_palette == palindex ) return;
3504 
3505 
3506     if(!level || palindex == 0 || palindex > level->numpalettes)
3507     {
3508         current_palette = 0;
3509     }
3510     else if(level)
3511     {
3512         current_palette = palindex;
3513     }
3514 }
3515 
3516 // Load colour 0-127 from data/pal.act
standard_palette(int immediate)3517 void standard_palette(int immediate)
3518 {
3519     unsigned char pp[MAX_PAL_SIZE] = {0};
3520     if(load_palette(pp, "data/pal.act"))
3521     {
3522         memcpy(pal, pp, (PAL_BYTES) / 2);
3523     }
3524     if(immediate)
3525     {
3526         change_system_palette(0);
3527     }
3528 }
3529 
3530 
unload_background()3531 void unload_background()
3532 {
3533     if (background)
3534     {
3535         clearscreen(background);
3536     }
3537 }
3538 
3539 
_makecolour(int r,int g,int b)3540 int _makecolour(int r, int g, int b)
3541 {
3542     return colour32(r, g, b);
3543 }
3544 
3545 // parses a color string in the format "R_G_B" or as a raw integer
parsecolor(const char * string)3546 int parsecolor(const char *string)
3547 {
3548     int r, g, b;
3549     if(strchr(string, '_') != strrchr(string, '_'))
3550     {
3551         // 2 underscores; color is in "R_G_B" format
3552         r = atoi(string);
3553         g = atoi(strchr(string, '_') + 1);
3554         b = atoi(strrchr(string, '_') + 1);
3555         return _makecolour(r, g, b);
3556     }
3557     else
3558     {
3559         return atoi(string);    // raw integer
3560     }
3561 }
3562 
3563 // ltb 1-17-05   new function for lifebar colors
lifebar_colors()3564 void lifebar_colors()
3565 {
3566     char *filename = "data/lifebar.txt";
3567     char *buf;
3568     size_t size;
3569     int pos;
3570     ArgList arglist;
3571     char argbuf[MAX_ARG_LEN + 1] = "";
3572 
3573 
3574     char *command;
3575 
3576     if(buffer_pakfile(filename, &buf, &size) != 1)
3577     {
3578         color_black = 0;
3579         color_red = 0;
3580         color_orange = 0;
3581         color_yellow = 0;
3582         color_white = 0;
3583         color_blue = 0;
3584         color_green = 0;
3585         color_pink = 0;
3586         color_purple = 0;
3587         color_magic = 0;
3588         color_magic2 = 0;
3589         shadowcolor = 0;
3590         shadowalpha = BLEND_MULTIPLY + 1;
3591         shadowopacity = 255;
3592         return;
3593     }
3594 
3595     pos = 0;
3596     colorbars = 1;
3597     while(pos < size)
3598     {
3599         if(ParseArgs(&arglist, buf + pos, argbuf))
3600         {
3601             command = GET_ARG(0);
3602             if(command && command[0])
3603             {
3604                 if(stricmp(command, "blackbox") == 0)
3605                 {
3606                     color_black = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3607                 }
3608                 else if(stricmp(command, "whitebox") == 0)
3609                 {
3610                     color_white = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3611                 }
3612                 else if(stricmp(command, "color300") == 0)
3613                 {
3614                     color_orange = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3615                 }
3616                 else if(stricmp(command, "color25") == 0)
3617                 {
3618                     color_red = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3619                 }
3620                 else if(stricmp(command, "color50") == 0)
3621                 {
3622                     color_yellow = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3623                 }
3624                 else if(stricmp(command, "color100") == 0)
3625                 {
3626                     color_green = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3627                 }
3628                 else if(stricmp(command, "color200") == 0)
3629                 {
3630                     color_blue = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3631                 }
3632                 else if(stricmp(command, "color400") == 0)
3633                 {
3634                     color_pink = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3635                 }
3636                 else if(stricmp(command, "color500") == 0)
3637                 {
3638                     color_purple = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3639                 }
3640                 //magic bars color declarations by tails
3641                 else if(stricmp(command, "colormagic") == 0)
3642                 {
3643                     color_magic = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3644                 }
3645                 else if(stricmp(command, "colormagic2") == 0)
3646                 {
3647                     color_magic2 = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3648                 }
3649                 //end of magic bars color declarations by tails
3650                 else if(stricmp(command, "shadowcolor") == 0)
3651                 {
3652                     shadowcolor = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
3653                 }
3654                 else if(stricmp(command, "shadowalpha") == 0) //gfxshadow alpha
3655                 {
3656                     shadowalpha = GET_INT_ARG(1);
3657                 }
3658                 else if(stricmp(command, "shadowopacity") == 0)
3659                 {
3660                     shadowopacity = GET_INT_ARG(1);
3661                 }
3662                 else if(command && command[0])
3663                 {
3664                     printf("Warning: Unknown command in lifebar.txt: '%s'.\n", command);
3665                 }
3666             }
3667         }
3668 
3669         // Go to next line
3670         pos += getNewLineStart(buf + pos);
3671     }
3672     if(buf != NULL)
3673     {
3674         free(buf);
3675         buf = NULL;
3676     }
3677 }
3678 // ltb 1-17-05 end new lifebar colors
3679 
3680 
init_colourtable()3681 void init_colourtable()
3682 {
3683     mpcolourtable[0]  = color_magic2;
3684     mpcolourtable[1]  = color_magic;
3685     mpcolourtable[2]  = color_magic;
3686     mpcolourtable[3]  = color_magic;
3687     mpcolourtable[4]  = color_magic2;
3688     mpcolourtable[5]  = color_magic;
3689     mpcolourtable[6]  = color_magic2;
3690     mpcolourtable[7]  = color_magic;
3691     mpcolourtable[8]  = color_magic2;
3692     mpcolourtable[9]  = color_magic;
3693     mpcolourtable[10] = color_magic2;
3694 
3695     hpcolourtable[0]  = color_purple;
3696     hpcolourtable[1]  = color_red;
3697     hpcolourtable[2]  = color_yellow;
3698     hpcolourtable[3]  = color_green;
3699     hpcolourtable[4]  = color_blue;
3700     hpcolourtable[5]  = color_orange;
3701     hpcolourtable[6]  = color_pink;
3702     hpcolourtable[7]  = color_purple;
3703     hpcolourtable[8]  = color_black;
3704     hpcolourtable[9]  = color_white;
3705     hpcolourtable[10] = color_white;
3706 
3707     memcpy(ldcolourtable, hpcolourtable, 11 * sizeof(*hpcolourtable));
3708 }
3709 
load_background(char * filename)3710 void load_background(char *filename)
3711 {
3712     // Clean up any previous background.
3713     unload_background();
3714 
3715     // Attempt to load 8bit color depth background. If it fails,
3716     // then attempt to load 24bit color depth background. If THAT
3717     // fails, something is wrong and we better shut down to avoid
3718     // a crash.
3719     if(!loadscreen(filename, packfile, NULL, PIXEL_x8, &background))
3720     {
3721         if (loadscreen32(filename, packfile, &background))
3722         {
3723             printf("Loaded 32-bit background '%s'\n", filename);
3724         }
3725         else
3726         {
3727             borShutdown(1, "Error loading background (PIXEL_x8/PIXEL_32) file '%s'", filename);
3728         }
3729     }
3730 
3731     // If background is 8bit color depth, use its color
3732     // table to populate the global and global neon palettes.
3733     if (background->pixelformat == PIXEL_x8)
3734     {
3735         memcpy(pal, background->palette, PAL_BYTES);
3736         memcpy(neontable, pal, PAL_BYTES);
3737     }
3738 
3739     lifebar_colors();
3740     if(!color_black)
3741     {
3742         color_black = _makecolour(0, 0, 0);    // black boxes 500-600HP
3743     }
3744     if(!color_red)
3745     {
3746         color_red = _makecolour(255, 0, 0);    // 1% - 25% Full Health
3747     }
3748     if(!color_orange)
3749     {
3750         color_orange = _makecolour(255, 150, 0);    // 200-300HP
3751     }
3752     if(!color_yellow)
3753     {
3754         color_yellow = _makecolour(0xF8, 0xB8, 0x40);    // 26%-50% Full health
3755     }
3756     if(!color_white)
3757     {
3758         color_white = _makecolour(255, 255, 255);    // white boxes 600+ HP
3759     }
3760     if(!color_blue)
3761     {
3762         color_blue = _makecolour(0, 0, 255);    // 100-200 HP
3763     }
3764     if(!color_green)
3765     {
3766         color_green = _makecolour(0, 255, 0);    // 51% - 100% full health
3767     }
3768     if(!color_pink)
3769     {
3770         color_pink = _makecolour(255, 0, 255);    // 300-400HP
3771     }
3772     if(!color_purple)
3773     {
3774         color_purple = _makecolour(128, 48, 208);    // transbox 400-500HP
3775     }
3776     if(!color_magic)
3777     {
3778         color_magic = _makecolour(98, 180, 255);    // 1st magic bar color by tails
3779     }
3780     if(!color_magic2)
3781     {
3782         color_magic2 = _makecolour(24, 48, 143);    // 2sec magic bar color by tails
3783     }
3784     if(!shadowcolor)
3785     {
3786         shadowcolor =  _makecolour(64, 64, 64);
3787     }
3788     init_colourtable();
3789 
3790     video_clearscreen();
3791     pal[0] = pal[1] = pal[2] = 0;
3792     //palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
3793     change_system_palette(0);
3794 }
3795 
load_cached_background(char * filename)3796 void load_cached_background(char *filename)
3797 {
3798 #ifndef CACHE_BACKGROUNDS
3799     load_background(filename);
3800 #else
3801     int index = -1;
3802     unload_background();
3803 
3804     if(strcmp(filename, "data/bgs/logo") == 0)
3805     {
3806         index = 0;
3807     }
3808     else if(strcmp(filename, "data/bgs/title") == 0)
3809     {
3810         index = 1;
3811     }
3812     else if(strcmp(filename, "data/bgs/titleb") == 0)
3813     {
3814         index = 2;
3815     }
3816     else if(strcmp(filename, "data/bgs/loading") == 0)
3817     {
3818         index = 3;
3819     }
3820     else if(strcmp(filename, "data/bgs/loading2") == 0)
3821     {
3822         index = 4;
3823     }
3824     else if(strcmp(filename, "data/bgs/hiscore") == 0)
3825     {
3826         index = 5;
3827     }
3828     else if(strcmp(filename, "data/bgs/complete") == 0)
3829     {
3830         index = 6;
3831     }
3832     else if(strcmp(filename, "data/bgs/unlockbg") == 0)
3833     {
3834         index = 7;
3835     }
3836     else if(strcmp(filename, "data/bgs/select") == 0)
3837     {
3838         index = 8;
3839     }
3840 
3841     if((index == -1) || (bg_cache[index] == NULL))
3842     {
3843         borShutdown(1, "Error: can't load cached background '%s'", filename);
3844     }
3845 
3846     if(background)
3847     {
3848         freescreen(&background);
3849     }
3850     background = allocscreen(videomodes.hRes, videomodes.vRes, bg_cache[index]->pixelformat);
3851     copyscreen(background, bg_cache[index]);
3852 
3853     if(background->pixelformat == PIXEL_8)
3854     {
3855         memcpy(pal, bg_palette_cache[index], PAL_BYTES);
3856     }
3857     else if(background->pixelformat == PIXEL_x8)
3858     {
3859         memcpy(background->palette, bg_cache[index]->palette, PAL_BYTES);
3860         memcpy(pal, background->palette, PAL_BYTES);
3861     }
3862 
3863     video_clearscreen();
3864     pal[0] = pal[1] = pal[2] = 0;
3865     //palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
3866     change_system_palette(0);
3867     printf("use cached bg\n");
3868 #endif
3869 }
3870 
3871 #ifdef CACHE_BACKGROUNDS
cache_background(char * filename)3872 void cache_background(char *filename)
3873 {
3874     s_screen *bg = allocscreen(videomodes.hRes, videomodes.vRes, pixelformat);
3875     int index = -1;
3876 
3877     if(pixelformat == PIXEL_8)
3878     {
3879         if(!loadscreen(filename, packfile, pal, pixelformat, &bg))
3880         {
3881             freescreen(&bg);
3882             bg = NULL;
3883         }
3884     }
3885     else if(pixelformat == PIXEL_x8)
3886     {
3887         if(!loadscreen(filename, packfile, NULL, pixelformat, &bg))
3888         {
3889             if(!loadscreen32(filename, packfile, &bg))
3890             {
3891                 freescreen(&bg);
3892                 bg = NULL;
3893             }
3894         }
3895     }
3896     else
3897     {
3898         borShutdown(1, "Error caching background, Unknown Pixel Format!\n");
3899     }
3900 
3901     if(strcmp(filename, "data/bgs/logo") == 0)
3902     {
3903         index = 0;
3904     }
3905     else if(strcmp(filename, "data/bgs/title") == 0)
3906     {
3907         index = 1;
3908     }
3909     else if(strcmp(filename, "data/bgs/titleb") == 0)
3910     {
3911         index = 2;
3912     }
3913     else if(strcmp(filename, "data/bgs/loading") == 0)
3914     {
3915         index = 3;
3916     }
3917     else if(strcmp(filename, "data/bgs/loading2") == 0)
3918     {
3919         index = 4;
3920     }
3921     else if(strcmp(filename, "data/bgs/hiscore") == 0)
3922     {
3923         index = 5;
3924     }
3925     else if(strcmp(filename, "data/bgs/complete") == 0)
3926     {
3927         index = 6;
3928     }
3929     else if(strcmp(filename, "data/bgs/unlockbg") == 0)
3930     {
3931         index = 7;
3932     }
3933     else if(strcmp(filename, "data/bgs/select") == 0)
3934     {
3935         index = 8;
3936     }
3937     else
3938     {
3939         borShutdown(1, "Error: unknown cached background '%s'", filename);
3940     }
3941 
3942     bg_cache[index] = bg;
3943 
3944     if(pixelformat == PIXEL_8)
3945     {
3946         memcpy(bg_palette_cache[index], pal, PAL_BYTES);
3947     }
3948 
3949     change_system_palette(0);
3950 }
3951 
cache_all_backgrounds()3952 void cache_all_backgrounds()
3953 {
3954     cache_background("data/bgs/logo");
3955     cache_background("data/bgs/title");
3956     cache_background("data/bgs/titleb");
3957     cache_background("data/bgs/loading2");
3958     cache_background("data/bgs/hiscore");
3959     cache_background("data/bgs/complete");
3960     cache_background("data/bgs/unlockbg");
3961     cache_background("data/bgs/select");
3962 }
3963 #endif
3964 
load_layer(char * filename,char * maskfilename,int index)3965 void load_layer(char *filename, char *maskfilename, int index)
3966 {
3967     if(!level)
3968     {
3969         return;
3970     }
3971 
3972     if(filename && level->layers[index].gfx.handle == NULL)
3973     {
3974         if(*maskfilename || ((level->layers[index].drawmethod.alpha > 0 || level->layers[index].drawmethod.transbg) && !level->layers[index].drawmethod.water.watermode))
3975         {
3976             // assume sprites are faster than screen when transparency or alpha are specified
3977             level->layers[index].gfx.sprite = loadsprite2(filename, &(level->layers[index].size.x), &(level->layers[index].size.y));
3978             if (*maskfilename)
3979             {
3980                 level->layers[index].gfx.sprite->mask = loadsprite2(maskfilename, &(level->layers[index].size.x), &(level->layers[index].size.y));
3981                 *maskfilename = 0; // clear mask filename so mask is only used for this one sprite
3982             }
3983         }
3984         else
3985         {
3986             // use screen for water effect for now, it should be faster than sprite
3987             // otherwise, a screen should be fine, especially in 8bit mode, it is super fast,
3988             //            or, at least it is not slower than a sprite
3989             if(loadscreen(filename, packfile, NULL, pixelformat, &level->layers[index].gfx.screen))
3990             {
3991                 level->layers[index].size.y = level->layers[index].gfx.screen->height;
3992                 level->layers[index].size.x = level->layers[index].gfx.screen->width;
3993             }
3994         }
3995     }
3996 
3997     if(filename && level->layers[index].gfx.handle == NULL)
3998     {
3999         borShutdown(1, "Error loading file '%s'", filename);
4000     }
4001     else
4002     {
4003         if(level->layers[index].drawmethod.xrepeat < 0)
4004         {
4005             level->layers[index].offset.x -= level->layers[index].size.x * 20000;
4006             level->layers[index].drawmethod.xrepeat = 40000;
4007         }
4008         if(level->layers[index].drawmethod.yrepeat < 0)
4009         {
4010             level->layers[index].offset.z -= level->layers[index].size.y * 20000;
4011             level->layers[index].drawmethod.yrepeat = 40000;
4012         }
4013         //printf("bglayer width=%d height=%d xoffset=%d zoffset=%d xrepeat=%d zrepeat%d\n", level->layers[index].size.x, level->layers[index].size.y, level->layers[index].offset.x, level->layers[index].offset.z, level->layers[index].xrepeat, level->layers[index].zrepeat);
4014     }
4015 
4016 }
4017 
4018 
loadsprite2(char * filename,int * width,int * height)4019 s_sprite *loadsprite2(char *filename, int *width, int *height)
4020 {
4021     size_t size;
4022     s_bitmap *bitmap = NULL;
4023     s_sprite *sprite = NULL;
4024     int clip_left;
4025     int clip_right;
4026     int clip_top;
4027     int clip_bottom;
4028 
4029     // Load raw bitmap (image) file from pack. If this
4030     // fails, then we return NULL.
4031     bitmap = loadbitmap(filename, packfile, pixelformat);
4032 
4033     if(!bitmap)
4034     {
4035         return NULL;
4036     }
4037 
4038     // Apply width and height adjustments, if any.
4039     if(width)
4040     {
4041         *width = bitmap->width;
4042     }
4043 
4044     if(height)
4045     {
4046         *height = bitmap->height;
4047     }
4048 
4049     // Trim empty pixels from the bitmap to save memory.
4050     // We will pass the arguments by reference - they will
4051     // be modified by the clipping function to tell us
4052     // exactly how much trim work on each axis was done.
4053     clipbitmap(bitmap, &clip_left, &clip_right, &clip_top, &clip_bottom);
4054 
4055     // Get size of trimmed bitmap and allocate memory for
4056     // use as a sprite. If this fails, then free the memory
4057     // bitmap occupies, and return NULL.
4058     size = fakey_encodesprite(bitmap);
4059     sprite = (s_sprite *)malloc(size);
4060 
4061     if(!sprite)
4062     {
4063         freebitmap(bitmap);
4064         return NULL;
4065     }
4066 
4067     // Transpose bitmap to a sprite, using the memory
4068     // we allocated for it above. The trim arguments
4069     // from our trimming function will be used as an
4070     // offset. We'll also store/ the bitmap's trimmed
4071     // dimensions for later use.
4072     encodesprite(-clip_left, -clip_top, bitmap, sprite);
4073     sprite->offsetx = clip_left;
4074     sprite->offsety = clip_top;
4075     sprite->srcwidth = bitmap->clipped_width;
4076     sprite->srcheight = bitmap->clipped_height;
4077 
4078     // Delete the raw bitmap, we don't need it
4079     // any more.
4080     freebitmap(bitmap);
4081 
4082     // Return encoded sprite.
4083     return sprite;
4084 }
4085 
4086 
4087 // Added to conserve memory
resourceCleanUp()4088 void resourceCleanUp()
4089 {
4090     freesprites();
4091     free_models();
4092     free_modelcache();
4093     load_special_sounds();
4094     load_script_setting();
4095     load_special_sprites();
4096     load_levelorder();
4097     load_models();
4098 }
4099 
freesprites()4100 void freesprites()
4101 {
4102     unsigned i;
4103     s_sprite_list *head;
4104     for(i = 0; i <= sprites_loaded; i++)
4105     {
4106         if(sprite_list != NULL)
4107         {
4108             free(sprite_list->sprite);
4109             sprite_list->sprite = NULL;
4110             free(sprite_list->filename);
4111             sprite_list->filename = NULL;
4112             head = sprite_list->next;
4113             free(sprite_list);
4114             sprite_list = head;
4115         }
4116     }
4117     if(sprite_map != NULL)
4118     {
4119         free(sprite_map);
4120         sprite_map = NULL;
4121     }
4122     sprites_loaded = 0;
4123 }
4124 
4125 // allocate enough members for sprite_map
prepare_sprite_map(size_t size)4126 void prepare_sprite_map(size_t size)
4127 {
4128     if(sprite_map == NULL || size + 1 > sprite_map_max_items )
4129     {
4130 #ifdef VERBOSE
4131         printf("%s %p\n", "prepare_sprite_map was", sprite_map);
4132 #endif
4133         sprite_map_max_items = (((size + 1) >> 8) + 1) << 8;
4134         sprite_map = realloc(sprite_map, sizeof(*sprite_map) * sprite_map_max_items);
4135         if(sprite_map == NULL)
4136         {
4137             borShutdown(1, "Out Of Memory!  Failed to create a new sprite_map\n");
4138         }
4139     }
4140 }
4141 
cachesound(int index,int load)4142 void cachesound(int index, int load)
4143 {
4144     if(index < 0)
4145     {
4146         return;
4147     }
4148     if(load)
4149     {
4150         sound_reload_sample(index);
4151     }
4152     else
4153     {
4154         sound_unload_sample(index);
4155     }
4156 }
4157 
4158 // Cachesprite
4159 // Unknown original date & author
4160 // Rewrite by Caskey, Damon V.
4161 // 2018-03-19
4162 //
4163 // Add or remove a sprite to the the sprite list
4164 // by index.
4165 //
4166 // index: Target index in the sprite list.
4167 // load: Load 1, or unload 0 the target sprite index.
cachesprite(int index,int load)4168 void cachesprite(int index, int load)
4169 {
4170     s_sprite *sprite;           // Sprite placeholder.
4171     s_sprite_list *map_node;    // Sprite map node placeholder.
4172 
4173     // Valid sprite list?
4174     if(sprite_map)
4175     {
4176         // Index argument valid?
4177         if(index >= 0)
4178         {
4179             // Index argument should be more than
4180             // the number of sprites loaded.
4181             if(index < sprites_loaded)
4182             {
4183                 // Get the sprite list node from sprite maps
4184                 // using our target index.
4185                 map_node = sprite_map[index].node;
4186 
4187                 // If load is true, then we want to load
4188                 // a sprite and assign it the target index.
4189                 // Otherwise, we want to free a sprite with
4190                 // target index.
4191                 if(load)
4192                 {
4193                     // Make sure there is not already
4194                     // a sprite with our target index.
4195                     sprite = map_node->sprite;
4196 
4197                     if(!sprite)
4198                     {
4199                         // Load the sprite file, then assign its
4200                         // new pointer to the sprite map using our
4201                         // index for the sprite map position.
4202                         sprite = loadsprite2(map_node->filename, NULL, NULL);
4203                         map_node->sprite = sprite;
4204                     }
4205                 }
4206                 else if(!load)
4207                 {
4208                     // Does the target sprite exist?
4209                     sprite = map_node->sprite;
4210 
4211                     if(sprite)
4212                     {
4213                         // Free the target sprite's resources, then remove
4214                         // its pointer from sprite map.
4215                         free(sprite);
4216                         map_node->sprite = NULL;
4217 
4218                         //printf("uncached sprite: %s\n", map_node->filename);
4219                     }
4220                 }
4221             }
4222         }
4223     }
4224 }
4225 
4226 // Returns sprite index.
4227 // Does not return on error, as it would shut the program down.
4228 // UT:
4229 // bmpformat - In 24bit mode, a sprite can have a 24bit palette(e.g., panel),
4230 //             so add this paramter to let sprite encoding function know.
4231 //             Actually the sprite pixel encoding method is the same, but a
4232 //             24bit palettte sprite should have a palette allocated at the end of
4233 //             pixel data, and the information is carried by the bitmap paramter.
loadsprite(char * filename,int ofsx,int ofsy,int bmpformat)4234 int loadsprite(char *filename, int ofsx, int ofsy, int bmpformat)
4235 {
4236     ptrdiff_t i, size, len;
4237     s_bitmap *bitmap = NULL;
4238     int clipl, clipr, clipt, clipb;
4239     s_sprite_list *curr = NULL, *head = NULL, *toshare = NULL;
4240 
4241     for(i = 0; i < sprites_loaded; i++)
4242     {
4243         if(sprite_map && sprite_map[i].node)
4244         {
4245             if(stricmp(sprite_map[i].node->filename, filename) == 0)
4246             {
4247                 if(!sprite_map[i].node->sprite)
4248                 {
4249                     sprite_map[i].node->sprite = loadsprite2(filename, NULL, NULL);
4250                 }
4251                 if(sprite_map[i].centerx + sprite_map[i].node->sprite->offsetx == ofsx &&
4252                         sprite_map[i].centery + sprite_map[i].node->sprite->offsety == ofsy)
4253                 {
4254                     return i;
4255                 }
4256                 else
4257                 {
4258                     toshare = sprite_map[i].node;
4259                 }
4260             }
4261         }
4262     }
4263 
4264     if(toshare)
4265     {
4266         prepare_sprite_map(sprites_loaded + 1);
4267         sprite_map[sprites_loaded].node = toshare;
4268         sprite_map[sprites_loaded].centerx = ofsx - toshare->sprite->offsetx;
4269         sprite_map[sprites_loaded].centery = ofsy - toshare->sprite->offsety;
4270         ++sprites_loaded;
4271         return sprites_loaded - 1;
4272     }
4273 
4274     bitmap = loadbitmap(filename, packfile, bmpformat);
4275     if(bitmap == NULL)
4276     {
4277         borShutdown(1, "Unable to load file '%s'\n", filename);
4278     }
4279 
4280     clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
4281 
4282     len = strlen(filename);
4283     size = fakey_encodesprite(bitmap);
4284     curr = malloc(sizeof(*curr));
4285     curr->sprite = malloc(size);
4286     curr->filename = malloc(len + 1);
4287     if(curr == NULL || curr->sprite == NULL || curr->filename == NULL)
4288     {
4289         freebitmap(bitmap);
4290         borShutdown(1, "loadsprite() Out of memory!\n");
4291     }
4292     memcpy(curr->filename, filename, len);
4293     curr->filename[len] = 0;
4294     encodesprite(ofsx - clipl, ofsy - clipt, bitmap, curr->sprite);
4295     if(sprite_list == NULL)
4296     {
4297         sprite_list = curr;
4298         sprite_list->next = NULL;
4299     }
4300     else
4301     {
4302         head = sprite_list;
4303         sprite_list = curr;
4304         sprite_list->next = head;
4305     }
4306     prepare_sprite_map(sprites_loaded + 1);
4307     sprite_map[sprites_loaded].node = sprite_list;
4308     sprite_map[sprites_loaded].centerx = ofsx - clipl;
4309     sprite_map[sprites_loaded].centery = ofsy - clipt;
4310     sprite_list->sprite->offsetx = clipl;
4311     sprite_list->sprite->offsety = clipt;
4312     sprite_list->sprite->srcwidth = bitmap->clipped_width;
4313     sprite_list->sprite->srcheight = bitmap->clipped_height;
4314     freebitmap(bitmap);
4315     ++sprites_loaded;
4316     return sprites_loaded - 1;
4317 }
4318 
load_special_sprites()4319 void load_special_sprites()
4320 {
4321     memset(shadowsprites, -1, sizeof(*shadowsprites) * 6);
4322     golsprite = gosprite = -1;
4323     if (testpackfile("data/sprites/shadow1.gif", packfile) >= 0 ||
4324         testpackfile("data/sprites/shadow1.png", packfile) >= 0)
4325     {
4326         shadowsprites[0] = loadsprite("data/sprites/shadow1", 9, 3, pixelformat);
4327     }
4328     if (testpackfile("data/sprites/shadow2.gif", packfile) >= 0 ||
4329         testpackfile("data/sprites/shadow2.png", packfile) >= 0)
4330     {
4331         shadowsprites[1] = loadsprite("data/sprites/shadow2", 14, 5, pixelformat);
4332     }
4333     if (testpackfile("data/sprites/shadow3.gif", packfile) >= 0 ||
4334         testpackfile("data/sprites/shadow3.png", packfile) >= 0)
4335     {
4336         shadowsprites[2] = loadsprite("data/sprites/shadow3", 19, 6, pixelformat);
4337     }
4338     if (testpackfile("data/sprites/shadow4.gif", packfile) >= 0 ||
4339         testpackfile("data/sprites/shadow4.png", packfile) >= 0)
4340     {
4341         shadowsprites[3] = loadsprite("data/sprites/shadow4", 24, 8, pixelformat);
4342     }
4343     if (testpackfile("data/sprites/shadow5.gif", packfile) >= 0 ||
4344         testpackfile("data/sprites/shadow5.png", packfile) >= 0)
4345     {
4346         shadowsprites[4] = loadsprite("data/sprites/shadow5", 29, 9, pixelformat);
4347     }
4348     if (testpackfile("data/sprites/shadow6.gif", packfile) >= 0 ||
4349         testpackfile("data/sprites/shadow6.png", packfile) >= 0)
4350     {
4351         shadowsprites[5] = loadsprite("data/sprites/shadow6", 34, 11, pixelformat);
4352     }
4353     if (testpackfile("data/sprites/arrow.gif", packfile) >= 0 ||
4354         testpackfile("data/sprites/arrow.png", packfile) >= 0)
4355     {
4356         gosprite  = loadsprite("data/sprites/arrow", 35, 23, pixelformat);
4357     }
4358     if (testpackfile("data/sprites/arrowl.gif", packfile) >= 0 ||
4359         testpackfile("data/sprites/arrowl.png", packfile) >= 0)
4360     {
4361         golsprite = loadsprite("data/sprites/arrowl", 35, 23, pixelformat);
4362     }
4363     if(timeicon_path[0])
4364     {
4365         timeicon = loadsprite(timeicon_path, 0, 0, pixelformat);
4366     }
4367     if(bgicon_path[0])
4368     {
4369         bgicon = loadsprite(bgicon_path, 0, 0, pixelformat);
4370     }
4371     if(olicon_path[0])
4372     {
4373         olicon = loadsprite(olicon_path, 0, 0, pixelformat);
4374     }
4375 }
4376 
unload_all_fonts()4377 void unload_all_fonts()
4378 {
4379     int i;
4380     for(i = 0; i < MAX_FONTS; i++)
4381     {
4382         font_unload(i);
4383     }
4384 }
4385 
load_all_fonts()4386 void load_all_fonts()
4387 {
4388     char path[MAX_BUFFER_LEN];
4389     int i;
4390 
4391     for(i = 0; i < MAX_FONTS; i++)
4392     {
4393         if(i == 0)
4394         {
4395             strcpy(path, "data/sprites/font");
4396         }
4397         else
4398         {
4399             sprintf(path, "%s%d", "data/sprites/font", i + 1);
4400         }
4401         if(font_load(i, path, packfile, fontmonospace[i] | fontmbs[i]))
4402         {
4403             // Plombo 3/1/2013: allow fonts to have alpha masks
4404             if(i == 0)
4405             {
4406                 strcpy(path, "data/sprites/fontmask");
4407             }
4408             else
4409             {
4410                 sprintf(path, "%s%d", "data/sprites/fontmask", i + 1);
4411             }
4412             if(font_loadmask(i, path, packfile, fontmonospace[i] | fontmbs[i]))
4413             {
4414                 printf("%d(m) ", i + 1);
4415             }
4416             else
4417             {
4418                 printf("%d ", i + 1);
4419             }
4420         }
4421     }
4422 }
4423 
translate_SDID(char * value)4424 int translate_SDID(char *value)
4425 {
4426     if(stricmp(value, "moveup") == 0)
4427     {
4428         return SDID_MOVEUP;
4429     }
4430     else if(stricmp(value, "movedown") == 0)
4431     {
4432         return SDID_MOVEDOWN;
4433     }
4434     else if(stricmp(value, "moveleft") == 0)
4435     {
4436         return SDID_MOVELEFT;
4437     }
4438     else if(stricmp(value, "moveright") == 0)
4439     {
4440         return SDID_MOVERIGHT;
4441     }
4442     else if(stricmp(value, "attack") == 0)
4443     {
4444         return SDID_ATTACK;
4445     }
4446     else if(stricmp(value, "attack2") == 0)
4447     {
4448         return SDID_ATTACK2;
4449     }
4450     else if(stricmp(value, "attack3") == 0)
4451     {
4452         return SDID_ATTACK3;
4453     }
4454     else if(stricmp(value, "attack4") == 0)
4455     {
4456         return SDID_ATTACK4;
4457     }
4458     else if(stricmp(value, "jump") == 0)
4459     {
4460         return SDID_JUMP;
4461     }
4462     else if(stricmp(value, "special") == 0)
4463     {
4464         return SDID_SPECIAL;
4465     }
4466     else if(stricmp(value, "start") == 0)
4467     {
4468         return SDID_START;
4469     }
4470     else if(stricmp(value, "screenshot") == 0)
4471     {
4472         return SDID_SCREENSHOT;
4473     }
4474     else if(stricmp(value, "esc") == 0)
4475     {
4476         return SDID_ESC;
4477     }
4478 
4479     return -1;
4480 }
4481 
load_menu_txt()4482 void load_menu_txt()
4483 {
4484     char *filename = "data/menu.txt";
4485     int pos, i;
4486     char *buf, *command;
4487     size_t size;
4488     ArgList arglist;
4489     char argbuf[MAX_ARG_LEN + 1] = "";
4490 
4491     // Read file
4492     if(buffer_pakfile(filename, &buf, &size) != 1)
4493     {
4494         return;
4495     }
4496 
4497     // Now interpret the contents of buf line by line
4498     pos = 0;
4499     while(pos < size)
4500     {
4501         if(ParseArgs(&arglist, buf + pos, argbuf))
4502         {
4503             command = GET_ARG(0);
4504             if(command && command[0])
4505             {
4506                 if(stricmp(command, "fontmonospace") == 0)
4507                 {
4508                     for(i = 0; i < MAX_FONTS; i++)
4509                     {
4510                         fontmonospace[i] = GET_INT_ARG((i + 1)) ? FONT_MONO : 0;
4511                     }
4512                 }
4513                 else if(stricmp(command, "fontmbs") == 0)
4514                 {
4515                     for(i = 0; i < MAX_FONTS; i++)
4516                     {
4517                         fontmbs[i] = GET_INT_ARG((i + 1)) ? FONT_MBS : 0;
4518                     }
4519                 }
4520             }
4521         }
4522 
4523         // Go to next line
4524         pos += getNewLineStart(buf + pos);
4525     }
4526 
4527     if(buf != NULL)
4528     {
4529         free(buf);
4530         buf = NULL;
4531     }
4532 }
4533 
load_special_sounds()4534 int load_special_sounds()
4535 {
4536     sound_unload_all_samples();
4537     SAMPLE_GO		= sound_load_sample("data/sounds/go.wav",		packfile,	0);
4538     SAMPLE_BEAT		= sound_load_sample("data/sounds/beat1.wav",	packfile,	0);
4539     SAMPLE_BLOCK	= sound_load_sample("data/sounds/block.wav",	packfile,	0);
4540     SAMPLE_FALL		= sound_load_sample("data/sounds/fall.wav",		packfile,	0);
4541     SAMPLE_GET		= sound_load_sample("data/sounds/get.wav",		packfile,	0);
4542     SAMPLE_GET2		= sound_load_sample("data/sounds/money.wav",	packfile,	0);
4543     SAMPLE_JUMP		= sound_load_sample("data/sounds/jump.wav",		packfile,	0);
4544     SAMPLE_INDIRECT	= sound_load_sample("data/sounds/indirect.wav",	packfile,	0);
4545     SAMPLE_PUNCH	= sound_load_sample("data/sounds/punch.wav",	packfile,	0);
4546     SAMPLE_1UP		= sound_load_sample("data/sounds/1up.wav",		packfile,	0);
4547     SAMPLE_TIMEOVER = sound_load_sample("data/sounds/timeover.wav", packfile,	0);
4548     SAMPLE_BEEP		= sound_load_sample("data/sounds/beep.wav",		packfile,	0);
4549     SAMPLE_BEEP2	= sound_load_sample("data/sounds/beep2.wav",	packfile,	0);
4550     SAMPLE_PAUSE	= sound_load_sample("data/sounds/pause.wav",	packfile,	0);
4551     SAMPLE_BIKE		= sound_load_sample("data/sounds/bike.wav",		packfile,	0);
4552 
4553     if ( SAMPLE_PAUSE < 0 ) SAMPLE_PAUSE = SAMPLE_BEEP2;
4554     if(SAMPLE_GO < 0 || SAMPLE_BEAT < 0 || SAMPLE_BLOCK < 0 ||
4555             SAMPLE_FALL < 0 || SAMPLE_GET < 0 || SAMPLE_GET2 < 0 ||
4556             SAMPLE_JUMP < 0 || SAMPLE_INDIRECT < 0 || SAMPLE_PUNCH < 0 ||
4557             SAMPLE_1UP < 0 || SAMPLE_TIMEOVER < 0 || SAMPLE_BEEP < 0 ||
4558             SAMPLE_BEEP2 < 0 || SAMPLE_PAUSE < 0 || SAMPLE_BIKE < 0)
4559     {
4560         return 0;
4561     }
4562     return 1;
4563 }
4564 
4565 // Caskey, Damon V.
4566 // 2019-01-02
4567 //
4568 // Return true if map_index matches a special purpose
4569 // map or falls within author defined hidden map range,
4570 // unless any of the above are same as default map (0).
is_map_hidden(s_model * model,int map_index)4571 int is_map_hidden(s_model *model, int map_index)
4572 {
4573 	// Have frozen map and it isn't same as default?
4574 	// If we do and it matches, return true.
4575 	if (model->maps.frozen > 0)
4576 	{
4577 		if (map_index == model->maps.frozen)
4578 		{
4579 			return 1;
4580 		}
4581 	}
4582 
4583 	// Check KO map. Same logic as frozen.
4584 	if (model->maps.ko > 0)
4585 	{
4586 		if (map_index == model->maps.ko)
4587 		{
4588 			return 1;
4589 		}
4590 	}
4591 
4592 	// Hidden map range. Both should be
4593 	// something other than default. If
4594 	// they are and map index is in range
4595 	// we return true.
4596 	if (model->maps.hide_start > 0
4597 		&& model->maps.hide_end > 0)
4598 	{
4599 		if (map_index >= model->maps.hide_start
4600 			&& map_index <= model->maps.hide_end)
4601 		{
4602 			return 1;
4603 		}
4604 	}
4605 
4606 	// If we got this far, there's no match.
4607 	return 0;
4608 }
4609 
4610 // Return model's next selectable map index in line.
nextcolourmap(s_model * model,int map_index)4611 int nextcolourmap(s_model *model, int map_index)
4612 {
4613 	// Increment to next color set, or return to 0
4614 	// if we go past number of available sets.
4615 	// Continue until we find an index that
4616 	// isn't hidden.
4617     do
4618     {
4619 		map_index++;
4620 
4621         if(map_index > model->maps_loaded)
4622         {
4623 			map_index = 0;
4624         }
4625     }
4626     while(is_map_hidden(model, map_index));
4627 
4628     return map_index;
4629 }
4630 
4631 // Increment to next map in player's (player_index) model
4632 // while avoiding the map another player with same
4633 // is using.
nextcolourmapn(s_model * model,int map_index,int player_index)4634 int nextcolourmapn(s_model *model, int map_index, int player_index)
4635 {
4636 	// Increment to next index.
4637 	map_index = nextcolourmap(model, map_index);
4638 
4639     s_set_entry *set = levelsets + current_set;
4640 
4641 	// If color selection is allowed but identical map is
4642 	// not (nosame 2), then let's make sure anohter player
4643 	// with same model isn't already using this map.
4644 	// If they are we'll find the next map available.
4645     if (colourselect && (set->nosame & 2))
4646     {
4647 		int i = 0;
4648 		int j = 0;
4649         int maps_count = model->maps_loaded + 1;
4650         int used_colors_map[maps_count];
4651         int used_color_count = 0;
4652 
4653         // Reset local used map array elements to 0.
4654 		for (i = 0; i < maps_count; i++)
4655 		{
4656 			used_colors_map[i] = 0;
4657 		}
4658 
4659         // Deduct hidden maps from map count.
4660 		if (model->maps.frozen > 0)
4661 		{
4662 			--maps_count;
4663 		}
4664 
4665 		if (model->maps.ko > 0)
4666 		{
4667 			--maps_count;
4668 		}
4669 
4670 		if (model->maps.hide_start > 0)
4671 		{
4672 			maps_count -= model->maps.hide_end - model->maps.hide_start + 1;
4673 		}
4674 
4675         // This logic attempts to populate used_colors_map array with
4676 		// every color in use by other players who picking same
4677 		// character. If there are aren't enough unused map indexes to
4678 		// go around (i.e. three players select a character that only
4679 		// has two maps), then we return initial map selection.
4680 
4681         for(i = 0; i < MAX_PLAYERS; i++)
4682         {
4683 			// Compare every player index to player_index argument. If
4684 			// it's a different index but that index's model matches
4685 			// player_index's model, then it's another player choosing
4686 			// (or about to choose) the same character.
4687 
4688             if (player_index != i
4689 				&&
4690 				stricmp(player[player_index].name, player[i].name) == 0)
4691             {
4692 				// Use the map index as an array element index, and mark it true.
4693 				// Now we now this map index is in use.
4694                 used_colors_map[player[i].colourmap] = 1;
4695 
4696 				// Increment number of used map indexes.
4697 				++used_color_count;
4698 
4699 				// If all the map indexes are used, we'll just
4700 				// have to settle for one we already picked.
4701 				if (used_color_count >= maps_count)
4702 				{
4703 					return map_index;
4704 				}
4705             }
4706         }
4707 
4708 		// Now that we have a list of used maps, let's employ it to
4709 		// find the first free map.
4710 		//
4711         // Loop to number of maps for the model. If our used_colors_map
4712 		// array element matching the map index doesn't have a true
4713 		// value, we can return the index.
4714 
4715         for(i = map_index, j = 0; j < maps_count; j++)
4716         {
4717             if (!used_colors_map[i])
4718             {
4719 				return i;
4720             }
4721 
4722             i = nextcolourmap(model, i);
4723         }
4724     }
4725 
4726 	// If we got here, then we couldn't find a free map index,
4727 	// so just return initial selection.
4728     return map_index;
4729 }
4730 
4731 // Return model's previous selectable map index in line.
prevcolourmap(s_model * model,int map_index)4732 int prevcolourmap(s_model *model, int map_index)
4733 {
4734 	// Decrement to previous color set, or return
4735 	// to last set if we go below 0. Continue until
4736 	// we find an index that isn't hidden.
4737     do
4738     {
4739 		map_index--;
4740         if(map_index < 0)
4741         {
4742 			map_index = model->maps_loaded;
4743         }
4744     }
4745     while(is_map_hidden(model, map_index));
4746 
4747     return map_index;
4748 }
4749 
4750 // Decrement to previous map in player's (player_index) model
4751 // while avoiding the map another player with same
4752 // is using.
prevcolourmapn(s_model * model,int map_index,int player_index)4753 int prevcolourmapn(s_model *model, int map_index, int player_index)
4754 {
4755 	// Decrement to previous index.
4756 	map_index = prevcolourmap(model, map_index);
4757 
4758 	s_set_entry *set = levelsets + current_set;
4759 
4760 	// If color selection is allowed but identical map is
4761 	// not (nosame 2), then let's make sure anohter player
4762 	// with same model isn't already using this map.
4763 	// If they are we'll find the next map available.
4764 	if (colourselect && (set->nosame & 2))
4765 	{
4766 		int i = 0;
4767 		int j = 0;
4768 		int maps_count = model->maps_loaded + 1;
4769 		int used_colors_map[maps_count];
4770 		int used_color_count = 0;
4771 
4772 		// Reset local used map array elements to 0.
4773 		for (i = 0; i < maps_count; i++)
4774 		{
4775 			used_colors_map[i] = 0;
4776 		}
4777 
4778 		// Deduct hidden maps from map count.
4779 		if (model->maps.frozen > 0)
4780 		{
4781 			--maps_count;
4782 		}
4783 
4784 		if (model->maps.ko > 0)
4785 		{
4786 			--maps_count;
4787 		}
4788 
4789 		if (model->maps.hide_start > 0)
4790 		{
4791 			maps_count -= model->maps.hide_end - model->maps.hide_start + 1;
4792 		}
4793 
4794 		// This logic attempts to populate used_colors_map array with
4795 		// every color in use by other players who picking same
4796 		// character. If there are aren't enough unused map indexes to
4797 		// go around (i.e. three players select a character that only
4798 		// has two maps), then we return initial map selection.
4799 
4800 		for (i = 0; i < MAX_PLAYERS; i++)
4801 		{
4802 			// Compare every player index to player_index argument. If
4803 			// it's a different index but that index's model matches
4804 			// player_index's model, then it's another player choosing
4805 			// (or about to choose) the same character.
4806 
4807 			if (player_index != i
4808 				&&
4809 				stricmp(player[player_index].name, player[i].name) == 0)
4810 			{
4811 				// Use the map index as an array element index, and mark it true.
4812 				// Now we now this map index is in use.
4813 				used_colors_map[player[i].colourmap] = 1;
4814 
4815 				// Increment number of used map indexes.
4816 				++used_color_count;
4817 
4818 				// If all the map indexes are used, we'll just
4819 				// have to settle for one we already picked.
4820 				if (used_color_count >= maps_count)
4821 				{
4822 					return map_index;
4823 				}
4824 			}
4825 		}
4826 
4827 		// Now that we have a list of used maps, let's employ it to
4828 		// find the first free map.
4829 		//
4830 		// Loop to number of maps for the model. If our used_colors_map
4831 		// array element matching the map index doesn't have a true
4832 		// value, we can return the index.
4833 
4834 		for (i = map_index, j = 0; j < maps_count; j++)
4835 		{
4836 			if (!used_colors_map[i])
4837 			{
4838 				return i;
4839 			}
4840 
4841 			i = prevcolourmap(model, i);
4842 		}
4843 	}
4844 
4845 	// If we got here, then we couldn't find a free map index,
4846 	// so just return initial selection.
4847 	return map_index;
4848 }
4849 
4850 // Caskey, Damon V.
4851 // 2019-01-02
4852 //
4853 // Return true if a model cache element is selectable by player.
is_model_cache_index_selectable(int cache_index)4854 int is_model_cache_index_selectable(int cache_index)
4855 {
4856 	// Must have selectable flag.
4857 	if (!model_cache[cache_index].selectable)
4858 	{
4859 		return 0;
4860 	}
4861 
4862 	// Element must contain a valid model.
4863 	if (!model_cache[cache_index].model)
4864 	{
4865 		return 0;
4866 	}
4867 
4868 	// Element's model must be selectable.
4869 	if (!is_model_selectable(model_cache[cache_index].model))
4870 	{
4871 		return 0;
4872 	}
4873 
4874 	// All checks passed. Return true.
4875 	return 1;
4876 }
4877 
4878 // Caskey, Damon V.
4879 // 2019-01-02
4880 //
4881 // Return true if a model is selectable by player.
is_model_selectable(s_model * model)4882 int is_model_selectable(s_model *model)
4883 {
4884 	// Must be a player type.
4885 	if (model->type != TYPE_PLAYER)
4886 	{
4887 		return 0;
4888 	}
4889 
4890 	// If model is marked secret, then secret
4891 	// characters must be allowed.
4892 	if (model->secret)
4893 	{
4894 		if (!allow_secret_chars)
4895 		{
4896 			return 0;
4897 		}
4898 	}
4899 
4900 	// 2019-01-02 DC: Not sure what this is.
4901 	// TO DO - Document clearcount vs. bonus.
4902 	if (model->clearcount > bonus)
4903 	{
4904 		return 0;
4905 	}
4906 
4907 	// Got this far, we can return true.
4908 	return 1;
4909 }
4910 
4911 // Caskey, Damon V.
4912 // 2019-01-03
4913 //
4914 // Return current number of player selectable models.
find_selectable_model_count()4915 int find_selectable_model_count()
4916 {
4917 	int result;
4918 	int i;
4919 
4920 	result = 0;
4921 
4922 	// Loop over model cache and increment
4923 	// count each time we find a selectable
4924 	// model.
4925 	for (i = 0; i < models_cached; i++)
4926 	{
4927 		if (is_model_cache_index_selectable(i))
4928 		{
4929 			++result;
4930 		}
4931 	}
4932 
4933 	return result;
4934 }
4935 
4936 // Use by player select menus
nextplayermodel(s_model * current)4937 s_model *nextplayermodel(s_model *current)
4938 {
4939     int i;
4940     int curindex = -1;
4941     int loops;
4942 
4943 	// Do we have a model?
4944 	if(current)
4945     {
4946         // Find index of current player model
4947         for(i = 0; i < models_cached; i++)
4948         {
4949             if(model_cache[i].model == current)
4950             {
4951                 curindex = i;
4952                 break;
4953             }
4954         }
4955     }
4956 
4957     // Find next player model (first one after current index)
4958     for(i = curindex + 1, loops = 0; loops < models_cached; i++, loops++)
4959     {
4960 		// Return to 0 if we've gone past the last model.
4961         if(i >= models_cached)
4962         {
4963             i = 0;
4964         }
4965 
4966 		// If valid and selectable, return the model.
4967         if(is_model_cache_index_selectable(i))
4968         {
4969 			//printf("next %s\n", model_cache[i].model->name);
4970 			return model_cache[i].model;
4971         }
4972     }
4973     borShutdown(1, "Fatal: can't find any player models!");
4974     return NULL;
4975 }
4976 
nextplayermodeln(s_model * current,int player_index)4977 s_model *nextplayermodeln(s_model *current, int player_index)
4978 {
4979     int i;
4980     s_set_entry *set = levelsets + current_set;
4981     s_model *model = nextplayermodel(current);
4982 
4983     if(set->nosame & 1)
4984     {
4985 		int used_player_count = 0;
4986 		int player_count = 0;
4987 
4988 		// Get number of selectable models.
4989 		player_count = find_selectable_model_count();
4990 
4991         // count all used player
4992         for(i = 0; model && i < MAX_PLAYERS; i++)
4993         {
4994             if(i != player_index
4995 				&& stricmp(player[player_index].name, player[i].name) == 0)
4996             {
4997                 ++used_player_count;
4998                 // all busy players? return the next natural
4999 				if (used_player_count >= player_count)
5000 				{
5001 					return model;
5002 				}
5003             }
5004         }
5005 
5006         // search the first free player
5007         for(i = 0; model && i < MAX_PLAYERS; i++)
5008         {
5009             if(i != player_index && stricmp(model->name, player[i].name) == 0)
5010             {
5011                 i = -1;
5012                 model = nextplayermodel(model);
5013             }
5014         }
5015     }
5016 
5017     return model;
5018 }
5019 
5020 // Use by player select menus
prevplayermodel(s_model * current)5021 s_model *prevplayermodel(s_model *current)
5022 {
5023     int i;
5024     int curindex = -1;
5025     int loops;
5026     if(current)
5027     {
5028         // Find index of current player model
5029         for(i = 0; i < models_cached; i++)
5030         {
5031             if(model_cache[i].model == current)
5032             {
5033                 curindex = i;
5034                 break;
5035             }
5036         }
5037     }
5038     // Find next player model (first one after current index)
5039     for(i = curindex - 1, loops = 0; loops < models_cached; i--, loops++)
5040     {
5041         if(i < 0)
5042         {
5043             i = models_cached - 1;
5044         }
5045 
5046 		// If valid and selectable, return the model.
5047         if(is_model_cache_index_selectable(i))
5048         {
5049             //printf("prev %s\n", model_cache[i].model->name);
5050             return model_cache[i].model;
5051         }
5052     }
5053     borShutdown(1, "Fatal: can't find any player models!");
5054     return NULL;
5055 }
5056 
prevplayermodeln(s_model * current,int player_index)5057 s_model *prevplayermodeln(s_model *current, int player_index)
5058 {
5059     int i;
5060     s_set_entry *set = levelsets + current_set;
5061     s_model *model = prevplayermodel(current);
5062 
5063     if(set->nosame & 1)
5064     {
5065 		int used_player_count = 0;
5066 		int player_count = 0;
5067 
5068 		// Get number of selectable models.
5069 		player_count = find_selectable_model_count();
5070 
5071         // count all used player
5072         for(i = 0; model && i < MAX_PLAYERS; i++)
5073         {
5074             if(i != player_index && stricmp(player[player_index].name, player[i].name) == 0)
5075             {
5076                 ++used_player_count;
5077                 // all busy players? return the prev natural
5078                 if (used_player_count >= player_count) return model;
5079             }
5080         }
5081 
5082         // search the first free player
5083         for(i = 0; model && i < MAX_PLAYERS; i++)
5084         {
5085             if(i != player_index && stricmp(model->name, player[i].name) == 0)
5086             {
5087                 i = -1;
5088                 model = prevplayermodel(model);
5089             }
5090         }
5091     }
5092 
5093     return model;
5094 }
5095 
5096 // Reset All Player Models to on/off for Select Screen.
reset_playable_list(char which)5097 static void reset_playable_list(char which)
5098 {
5099     int i;
5100     for(i = 0; i < models_cached; i++)
5101     {
5102         if(!which || (model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER))
5103         {
5104             model_cache[i].selectable = which;
5105         }
5106     }
5107 }
5108 
5109 // Specify which Player Models are allowable for selecting
load_playable_list(char * buf)5110 static void load_playable_list(char *buf)
5111 {
5112     int i, index;
5113     char *value;
5114     s_model *playermodels = NULL;
5115     ArgList arglist;
5116     char argbuf[MAX_ALLOWSELECT_LEN] = "";
5117 
5118     ParseArgs(&arglist, buf, argbuf);
5119 
5120     // avoid to load characters if there isn't an allowselect
5121     if ( stricmp(value = GET_ARG(0), "allowselect") != 0 ) return;
5122 
5123     reset_playable_list(0);
5124 
5125     for(i = 0; i < sizeof(argbuf); i++) allowselect_args[i] = ' ';
5126     for(i = 0; i < sizeof(argbuf); i++)
5127     {
5128         if ( argbuf[i] != '\0' ) allowselect_args[i] = argbuf[i]; // store allowselect players for savefile
5129         else allowselect_args[i] = ' ';
5130     }
5131     allowselect_args[sizeof(argbuf)-1] = '\0';
5132 
5133     for(i = 1; (value = GET_ARG(i))[0]; i++)
5134     {
5135         playermodels = findmodel(value);
5136         //if(playermodels == NULL) borShutdown(1, "Player model '%s' is not loaded.\n", value);
5137         index = get_cached_model_index(playermodels->name);
5138         if(index == -1)
5139         {
5140             borShutdown(1, "Player model '%s' is not cached.\n", value);
5141         }
5142         model_cache[index].selectable = 1;
5143     }
5144 
5145     return;
5146 }
5147 
alloc_specials(s_model * newchar)5148 void alloc_specials(s_model *newchar)
5149 {
5150     newchar->special = realloc(newchar->special, sizeof(s_com) * (newchar->specials_loaded + 1));
5151     memset(newchar->special + newchar->specials_loaded, 0, sizeof(s_com));
5152 }
5153 
alloc_frames(s_anim * anim,int fcount)5154 void alloc_frames(s_anim *anim, int fcount)
5155 {
5156     anim->sprite = malloc(fcount * sizeof(*anim->sprite));
5157     anim->delay = malloc(fcount * sizeof(*anim->delay));
5158     anim->vulnerable = malloc(fcount * sizeof(*anim->vulnerable));
5159     memset(anim->sprite, 0, fcount * sizeof(*anim->sprite));
5160     memset(anim->delay, 0, fcount * sizeof(*anim->delay));
5161     memset(anim->vulnerable, 0, fcount * sizeof(*anim->vulnerable));
5162 }
5163 
free_frames(s_anim * anim)5164 void free_frames(s_anim *anim)
5165 {
5166     int i, instance;
5167 
5168     if(anim->offset)
5169     {
5170         for(i = 0; i < anim->numframes; i++)
5171         {
5172             if(anim->offset[i])
5173             {
5174                 free(anim->offset[i]);
5175                 anim->offset[i] = NULL;
5176             }
5177         }
5178         free(anim->offset);
5179         anim->offset = NULL;
5180     }
5181 
5182     if(anim->idle)
5183     {
5184         free(anim->idle);
5185         anim->idle = NULL;
5186     }
5187 
5188     if(anim->move)
5189     {
5190         for(i = 0; i < anim->numframes; i++)
5191         {
5192             if(anim->move[i])
5193             {
5194                 free(anim->move[i]);
5195                 anim->move[i] = NULL;
5196             }
5197         }
5198         free(anim->move);
5199         anim->move = NULL;
5200     }
5201 
5202     if(anim->delay)
5203     {
5204         free(anim->delay);
5205         anim->delay = NULL;
5206     }
5207     if(anim->sprite)
5208     {
5209         free(anim->sprite);
5210         anim->sprite = NULL;
5211     }
5212     if(anim->platform)
5213     {
5214         free(anim->platform);
5215         anim->platform = NULL;
5216     }
5217     if(anim->vulnerable)
5218     {
5219         free(anim->vulnerable);
5220         anim->vulnerable = NULL;
5221     }
5222     if(anim->collision_body)
5223     {
5224         for(i = 0; i < anim->numframes; i++)
5225         {
5226             if(anim->collision_body[i])
5227             {
5228                 // Check each instance and free memory as needed.
5229                 // Momma always said put your toys away when you're done!
5230                 for(instance = 0; instance < max_collisons; instance++)
5231                 {
5232                     if(anim->collision_body[i]->instance[instance])
5233                     {
5234                         // First free any pointers allocated
5235                         // for sub structures.
5236 
5237                         // Coords.
5238                         if(anim->collision_body[i]->instance[instance]->coords)
5239                         {
5240                             free(anim->collision_body[i]->instance[instance]->coords);
5241                             anim->collision_body[i]->instance[instance]->coords = NULL;
5242                         }
5243 
5244                         free(anim->collision_body[i]->instance[instance]);
5245                         anim->collision_body[i]->instance[instance] = NULL;
5246                     }
5247                 }
5248 
5249                 free(anim->collision_body[i]);
5250                 anim->collision_body[i] = NULL;
5251             }
5252         }
5253         free(anim->collision_body);
5254         anim->collision_body = NULL;
5255     }
5256     if(anim->collision_entity)
5257     {
5258         for(i = 0; i < anim->numframes; i++)
5259         {
5260             if(anim->collision_entity[i])
5261             {
5262                 // Check each instance and free memory as needed.
5263                 // Momma always said put your toys away when you're done!
5264                 for(instance = 0; instance < max_collisons; instance++)
5265                 {
5266                     if(anim->collision_entity[i]->instance[instance])
5267                     {
5268                         // First free any pointers allocated
5269                         // for sub structures.
5270 
5271                         // Coords.
5272                         if(anim->collision_entity[i]->instance[instance]->coords)
5273                         {
5274                             free(anim->collision_entity[i]->instance[instance]->coords);
5275                             anim->collision_entity[i]->instance[instance]->coords = NULL;
5276                         }
5277 
5278                         free(anim->collision_entity[i]->instance[instance]);
5279                         anim->collision_entity[i]->instance[instance] = NULL;
5280                     }
5281                 }
5282 
5283                 free(anim->collision_entity[i]);
5284                 anim->collision_entity[i] = NULL;
5285             }
5286         }
5287         free(anim->collision_entity);
5288         anim->collision_entity = NULL;
5289     }
5290     if(anim->shadow)
5291     {
5292         free(anim->shadow);
5293         anim->shadow = NULL;
5294     }
5295     if(anim->shadow_coords)
5296     {
5297         free(anim->shadow_coords);
5298         anim->shadow_coords = NULL;
5299     }
5300     if(anim->soundtoplay)
5301     {
5302         free(anim->soundtoplay);
5303         anim->soundtoplay = NULL;
5304     }
5305     if(anim->collision_attack)
5306     {
5307         for(i = 0; i < anim->numframes; i++)
5308         {
5309             if(anim->collision_attack[i])
5310             {
5311                 // Check each attack instance and free memory as needed.
5312                 // Momma always said put your toys away when you're done!
5313                 for(instance = 0; instance < max_collisons; instance++)
5314                 {
5315                     if(anim->collision_attack[i]->instance[instance])
5316                     {
5317                         // First free any pointers allocated
5318                         // for sub structures.
5319 
5320                         // Coords.
5321                         if(anim->collision_attack[i]->instance[instance]->coords)
5322                         {
5323                             free(anim->collision_attack[i]->instance[instance]->coords);
5324                             anim->collision_attack[i]->instance[instance]->coords = NULL;
5325                         }
5326 
5327                         // Recursive damage.
5328                         if(anim->collision_attack[i]->instance[instance]->recursive)
5329                         {
5330                             free(anim->collision_attack[i]->instance[instance]->recursive);
5331                             anim->collision_attack[i]->instance[instance]->recursive = NULL;
5332                         }
5333 
5334                         free(anim->collision_attack[i]->instance[instance]);
5335                         anim->collision_attack[i]->instance[instance] = NULL;
5336                     }
5337                 }
5338 
5339                 free(anim->collision_attack[i]);
5340                 anim->collision_attack[i] = NULL;
5341             }
5342         }
5343         free(anim->collision_attack);
5344         anim->collision_attack = NULL;
5345     }
5346     if(anim->drawmethods)
5347     {
5348         for(i = 0; i < anim->numframes; i++)
5349         {
5350             if(anim->drawmethods[i])
5351             {
5352                 free(anim->drawmethods[i]);
5353                 anim->drawmethods[i] = NULL;
5354             }
5355         }
5356         free(anim->drawmethods);
5357         anim->drawmethods = NULL;
5358     }
5359 }
5360 
5361 #if 0
5362 s_anim_list *anim_list_delete(s_anim_list *list, int index)
5363 {
5364     if(list == NULL)
5365     {
5366         return NULL;
5367     }
5368     if(list->anim->model_index == index)
5369     {
5370         s_anim_list *next;
5371         next = list->next;
5372         free_anim(list->anim);
5373         free(list);
5374         --anims_loaded;
5375         return next;
5376     }
5377     list->next = anim_list_delete(list->next, index);
5378     return list;
5379 }
5380 #endif
5381 
anim_list_delete(int index)5382 void anim_list_delete(int index)
5383 {
5384     s_anim_list head;
5385     head.next = anim_list;
5386     s_anim_list *list = &head;
5387     while(list && list->next)
5388     {
5389         if(list->next->anim->model_index == index)
5390         {
5391             s_anim_list *next = list->next->next;
5392             free_anim(list->next->anim);
5393             if(list->next == anim_list)
5394             {
5395                 anim_list = next;
5396             }
5397             free(list->next);
5398             --anims_loaded;
5399             list->next = next;
5400         }
5401         else
5402         {
5403             list = list->next;
5404         }
5405     }
5406 }
5407 
free_anim(s_anim * anim)5408 void free_anim(s_anim *anim)
5409 {
5410     if(!anim)
5411     {
5412         return;
5413     }
5414     free_frames(anim);
5415     if(anim->starvelocity)
5416     {
5417         free(anim->starvelocity);
5418         anim->starvelocity = NULL;
5419     }
5420     if(anim->weaponframe)
5421     {
5422         free(anim->weaponframe);
5423         anim->weaponframe = NULL;
5424     }
5425     if(anim->spawnframe)
5426     {
5427         free(anim->spawnframe);
5428         anim->spawnframe = NULL;
5429     }
5430     if(anim->summonframe)
5431     {
5432         free(anim->summonframe);
5433         anim->summonframe = NULL;
5434     }
5435     if(anim->counterrange)
5436     {
5437         free(anim->counterrange);
5438         anim->counterrange = NULL;
5439     }
5440 
5441     if(anim->dropframe)
5442     {
5443         free(anim->dropframe);
5444         anim->dropframe = NULL;
5445     }
5446 
5447     if(anim->jumpframe)
5448     {
5449         free(anim->jumpframe);
5450         anim->jumpframe = NULL;
5451     }
5452 
5453     if(anim->landframe)
5454     {
5455         free(anim->landframe);
5456         anim->landframe = NULL;
5457     }
5458 
5459     if(anim->energycost)
5460     {
5461         free(anim->energycost);
5462         anim->energycost = NULL;
5463     }
5464     free(anim);
5465 }
5466 
hasFreetype(s_model * m,e_ModelFreetype t)5467 int hasFreetype(s_model *m, e_ModelFreetype t)
5468 {
5469     assert(m);
5470     return (m->freetypes & t) == t;
5471 }
5472 
addFreeType(s_model * m,e_ModelFreetype t)5473 void addFreeType(s_model *m, e_ModelFreetype t)
5474 {
5475     assert(m);
5476     m->freetypes |= t;
5477 }
5478 
cache_model_sprites(s_model * m,int ld)5479 void cache_model_sprites(s_model *m, int ld)
5480 {
5481     int i, f, instance;
5482     s_anim *anim;
5483     cachesprite(m->icon.def, ld);
5484     cachesprite(m->icon.die, ld);
5485     cachesprite(m->icon.get, ld);
5486     cachesprite(m->icon.mphigh, ld);
5487     cachesprite(m->icon.mplow, ld);
5488     cachesprite(m->icon.mpmed, ld);
5489     cachesprite(m->icon.pain, ld);
5490     cachesprite(m->icon.weapon, ld);
5491     cachesound(m->diesound, ld);
5492     for(i = 0; i < MAX_PLAYERS; i++)
5493     {
5494         cachesprite(m->parrow[i][0], ld);
5495     }
5496 
5497     //if(hasFreetype(model, MF_ANIMLIST)){
5498     for(i = 0; i < max_animations; i++)
5499     {
5500         anim = m->animation[i];
5501         if(anim)
5502         {
5503             for(f = 0; f < anim->numframes; f++)
5504             {
5505                 cachesprite(anim->sprite[f], ld);
5506                 if(anim->soundtoplay)
5507                 {
5508                     cachesound(anim->soundtoplay[f], ld);
5509                 }
5510                 if(anim->collision_attack && anim->collision_attack[f])
5511                 {
5512                     for(instance = 0; instance < max_collisons; instance++)
5513                     {
5514                         cachesound(anim->collision_attack[f]->instance[instance]->hitsound, ld);
5515                         cachesound(anim->collision_attack[f]->instance[instance]->blocksound, ld);
5516                     }
5517                 }
5518             }
5519         }
5520     }
5521 }
5522 
5523 // Unload single model from memory
free_model(s_model * model)5524 int free_model(s_model *model)
5525 {
5526     int i;
5527     if(!model)
5528     {
5529         return 0;
5530     }
5531     printf("Unload '%s' ", model->name);
5532 
5533     if(hasFreetype(model, MF_ANIMLIST))
5534     {
5535         anim_list_delete(model->index);
5536     }
5537 
5538     printf(".");
5539 
5540     if(hasFreetype(model, MF_COLOURMAP))
5541     {
5542         for(i = 0; i < model->maps_loaded; i++)
5543         {
5544             if(model->colourmap[i] != NULL)
5545             {
5546                 free(model->colourmap[i]);
5547                 model->colourmap[i] = NULL;
5548             }
5549         }
5550         if(model->colourmap)
5551         {
5552             free(model->colourmap);
5553         }
5554         model->colourmap = NULL;
5555         model->maps_loaded = 0;
5556     }
5557 
5558     printf(".");
5559 
5560     if(hasFreetype(model, MF_PALETTE) && model->palette)
5561     {
5562         free(model->palette);
5563         model->palette = NULL;
5564     }
5565     printf(".");
5566     if(hasFreetype(model, MF_WEAPONS) && model->weapon && model->ownweapons)
5567     {
5568         free(model->weapon);
5569         model->weapon = NULL;
5570     }
5571     printf(".");
5572     if(hasFreetype(model, MF_BRANCH) && model->branch)
5573     {
5574         free(model->branch);
5575         model->branch = NULL;
5576     }
5577     printf(".");
5578     if(hasFreetype(model, MF_ANIMATION) && model->animation)
5579     {
5580         free(model->animation);
5581         model->animation = NULL;
5582     }
5583     printf(".");
5584     if(hasFreetype(model, MF_DEFENSE) && model->defense)
5585     {
5586         free(model->defense);
5587         model->defense = NULL;
5588     }
5589     printf(".");
5590     if(hasFreetype(model, MF_OFF_FACTORS) && model->offense_factors)
5591     {
5592         free(model->offense_factors);
5593         model->offense_factors = NULL;
5594     }
5595     printf(".");
5596     if(hasFreetype(model, MF_SPECIAL) && model->special)
5597     {
5598         free(model->special);
5599         model->special = NULL;
5600     }
5601     printf(".");
5602     if(hasFreetype(model, MF_SMARTBOMB) && model->smartbomb)
5603     {
5604         free(model->smartbomb);
5605         model->smartbomb = NULL;
5606     }
5607     printf(".");
5608 
5609     if(hasFreetype(model, MF_SCRIPTS))
5610     {
5611         clear_all_scripts(model->scripts, 2);
5612         free_all_scripts(&model->scripts);
5613     }
5614     printf(".");
5615 
5616     model_cache[model->index].model = NULL;
5617     deleteModel(model->name);
5618     printf(".");
5619 
5620     printf("Done.\n");
5621 
5622     return models_loaded--;
5623 }
5624 
5625 // Unload all models and animations memory
free_models()5626 void free_models()
5627 {
5628     s_model *temp;
5629 
5630     while((temp = getFirstModel()))
5631     {
5632         free_model(temp);
5633     }
5634 
5635     // free animation ids
5636     if(animdowns)
5637     {
5638         free(animdowns);
5639         animdowns          = NULL;
5640     }
5641     if(animups)
5642     {
5643         free(animups);
5644         animups            = NULL;
5645     }
5646     if(animbackwalks)
5647     {
5648         free(animbackwalks);
5649         animbackwalks      = NULL;
5650     }
5651     if(animwalks)
5652     {
5653         free(animwalks);
5654         animwalks          = NULL;
5655     }
5656     if(animidles)
5657     {
5658         free(animidles);
5659         animidles          = NULL;
5660     }
5661     if(animspecials)
5662     {
5663         free(animspecials);
5664         animspecials       = NULL;
5665     }
5666     if(animattacks)
5667     {
5668         free(animattacks);
5669         animattacks        = NULL;
5670     }
5671     if(animfollows)
5672     {
5673         free(animfollows);
5674         animfollows        = NULL;
5675     }
5676     if(animpains)
5677     {
5678         free(animpains);
5679         animpains          = NULL;
5680     }
5681     if(animbackpains)
5682     {
5683         free(animbackpains);
5684         animbackpains      = NULL;
5685     }
5686     if(animfalls)
5687     {
5688         free(animfalls);
5689         animfalls          = NULL;
5690     }
5691     if(animbackfalls)
5692     {
5693         free(animbackfalls);
5694         animbackfalls      = NULL;
5695     }
5696     if(animrises)
5697     {
5698         free(animrises);
5699         animrises          = NULL;
5700     }
5701     if(animbackrises)
5702     {
5703         free(animbackrises);
5704         animbackrises          = NULL;
5705     }
5706     if(animriseattacks)
5707     {
5708         free(animriseattacks);
5709         animriseattacks    = NULL;
5710     }
5711     if(animbackriseattacks)
5712     {
5713         free(animbackriseattacks);
5714         animbackriseattacks    = NULL;
5715     }
5716     if(animblkpains)
5717     {
5718         free(animblkpains);
5719         animblkpains       = NULL;
5720     }
5721     if(animbackblkpains)
5722     {
5723         free(animbackblkpains);
5724         animbackblkpains       = NULL;
5725     }
5726     if(animdies)
5727     {
5728         free(animdies);
5729         animdies           = NULL;
5730     }
5731     if(animbackdies)
5732     {
5733         free(animbackdies);
5734         animbackdies        = NULL;
5735     }
5736 }
5737 
5738 
alloc_anim()5739 s_anim *alloc_anim()
5740 {
5741     static int animindex = 0;
5742     s_anim_list *curr = NULL, *head = NULL;
5743     curr = malloc(sizeof(*curr));
5744     curr->anim = malloc(sizeof(*curr->anim));
5745     if(curr == NULL || curr->anim == NULL)
5746     {
5747         return NULL;
5748     }
5749     memset(curr->anim, 0, sizeof(*curr->anim));
5750     curr->anim->index = animindex++;
5751     if(anim_list == NULL)
5752     {
5753         anim_list = curr;
5754         anim_list->next = NULL;
5755     }
5756     else
5757     {
5758         head = anim_list;
5759         anim_list = curr;
5760         anim_list->next = head;
5761     }
5762     ++anims_loaded;
5763     return anim_list->anim;
5764 }
5765 
5766 // Caskey, Damon V.
5767 // 2016-11-27
5768 //
5769 // Allocate a collision attack instance, copy
5770 // property data if present, and return pointer.
collision_alloc_attack_instance(s_collision_attack * properties)5771 s_collision_attack *collision_alloc_attack_instance(s_collision_attack *properties)
5772 {
5773     s_collision_attack  *result;
5774     size_t              alloc_size;
5775 
5776     // Get amount of memory we'll need.
5777     alloc_size = sizeof(*result);
5778 
5779     // Allocate memory and get pointer.
5780     result = malloc(alloc_size);
5781 
5782     // If previous data is provided,
5783     // copy into new allocation.
5784     if(properties)
5785     {
5786         memcpy(result, properties, alloc_size);
5787     }
5788 
5789     // return result.
5790     return result;
5791 }
5792 
5793 // Caskey, Damon V.
5794 // 2016-11-27
5795 //
5796 // Allocate an empty collision attack list.
collision_alloc_attack_list()5797 s_collision_attack **collision_alloc_attack_list()
5798 {
5799     s_collision_attack **result;
5800     size_t             alloc_size;
5801 
5802     // Get amount of memory we'll need.
5803     alloc_size = max_collisons * sizeof(*result);
5804 
5805     // Allocate memory and get pointer.
5806     result = malloc(alloc_size);
5807 
5808     // Make sure the list is blank.
5809     memset(result, 0, alloc_size);
5810 
5811     // return result.
5812     return result;
5813 }
5814 
5815 // Caskey, Damon V.
5816 // 2016-11-27
5817 //
5818 // Allocate a collision body instance, copy
5819 // property data if present, and return pointer.
collision_alloc_body_instance(s_collision_body * properties)5820 s_collision_body *collision_alloc_body_instance(s_collision_body *properties)
5821 {
5822     s_collision_body    *result;
5823     size_t              alloc_size;
5824 
5825     // Get amount of memory we'll need.
5826     alloc_size = sizeof(*result);
5827 
5828     // Allocate memory and get pointer.
5829     result = malloc(alloc_size);
5830 
5831     // If previous data is provided,
5832     // copy into new allocation.
5833     if(properties)
5834     {
5835         memcpy(result, properties, alloc_size);
5836     }
5837 
5838     // return result.
5839     return result;
5840 }
5841 
5842 // Caskey, Damon V.
5843 // 2016-11-27
5844 //
5845 // Allocate an empty collision attack list.
collision_alloc_body_list()5846 s_collision_body **collision_alloc_body_list()
5847 {
5848     s_collision_body **result;
5849     size_t             alloc_size;
5850 
5851     // Get amount of memory we'll need.
5852     alloc_size = max_collisons * sizeof(*result);
5853 
5854     // Allocate memory and get pointer.
5855     result = malloc(alloc_size);
5856 
5857     // Make sure the list is blank.
5858     memset(result, 0, alloc_size);
5859 
5860     // return result.
5861     return result;
5862 }
5863 
5864 // Allocate a collision entity instance, copy
5865 // property data if present, and return pointer.
collision_alloc_entity_instance(s_collision_entity * properties)5866 s_collision_entity *collision_alloc_entity_instance(s_collision_entity *properties)
5867 {
5868     s_collision_entity    *result;
5869     size_t              alloc_size;
5870 
5871     // Get amount of memory we'll need.
5872     alloc_size = sizeof(*result);
5873 
5874     // Allocate memory and get pointer.
5875     result = malloc(alloc_size);
5876 
5877     // If previous data is provided,
5878     // copy into new allocation.
5879     if(properties)
5880     {
5881         memcpy(result, properties, alloc_size);
5882     }
5883 
5884     // return result.
5885     return result;
5886 }
5887 
5888 // Allocate an empty collision entity list.
collision_alloc_entity_list()5889 s_collision_entity **collision_alloc_entity_list()
5890 {
5891     s_collision_entity **result;
5892     size_t             alloc_size;
5893 
5894     // Get amount of memory we'll need.
5895     alloc_size = sizeof(*result);
5896 
5897     // Allocate memory and get pointer.
5898     result = malloc(alloc_size);
5899 
5900     // Make sure the list is blank.
5901     memset(result, 0, alloc_size);
5902 
5903     // return result.
5904     return result;
5905 }
5906 
5907 // Caskey, Damon V.
5908 // 2016-11-26
5909 //
5910 // Allocate collision coordinates, copy coords
5911 // data if present, and return pointer.
collision_alloc_coords(s_hitbox * coords)5912 s_hitbox *collision_alloc_coords(s_hitbox *coords)
5913 {
5914     s_hitbox    *result;
5915     size_t      alloc_size;
5916 
5917     // Get amount of memory we'll need.
5918     alloc_size = sizeof(*result);
5919 
5920     // Allocate memory and get pointer.
5921     result = malloc(alloc_size);
5922 
5923     // If previous data is provided,
5924     // copy into new allocation.
5925     if(coords)
5926     {
5927         memcpy(result, coords, alloc_size);
5928     }
5929 
5930     // Return result.
5931     return result;
5932 }
5933 
addframe(s_anim * a,int spriteindex,int framecount,int delay,unsigned idle,s_collision_entity * ebox,s_collision_body * bbox,s_collision_attack * attack,s_move * move,float * platform,int frameshadow,int * shadow_coords,int soundtoplay,s_drawmethod * drawmethod,s_axis_plane_vertical_int * offset,s_damage_recursive * recursive,s_hitbox * attack_coords,s_hitbox * body_coords,s_hitbox * entity_coords)5934 int addframe(s_anim             *a,
5935              int                spriteindex,
5936              int                framecount,
5937              int                delay,
5938              unsigned           idle,
5939              s_collision_entity *ebox,
5940              s_collision_body   *bbox,
5941              s_collision_attack *attack,
5942              s_move             *move,
5943              float              *platform,
5944              int                frameshadow,
5945              int                *shadow_coords,
5946              int                soundtoplay,
5947              s_drawmethod       *drawmethod,
5948              s_axis_plane_vertical_int        *offset,
5949              s_damage_recursive *recursive,
5950              s_hitbox           *attack_coords,
5951              s_hitbox           *body_coords,
5952              s_hitbox           *entity_coords)
5953 {
5954     int     i;
5955     size_t  size_col_on_frame,
5956             size_col_on_frame_struct;
5957 
5958     s_collision_attack  *collision_attack;
5959     s_collision_body    *collision_body;
5960     s_collision_entity  *collision_entity;
5961 
5962     ptrdiff_t currentframe;
5963     if(framecount > 0)
5964     {
5965         alloc_frames(a, framecount);
5966     }
5967     else
5968     {
5969         framecount = -framecount;    // for alloc method, use a negative value
5970     }
5971 
5972     currentframe = a->numframes;
5973     ++a->numframes;
5974 
5975     a->sprite[currentframe] = spriteindex;
5976     a->delay[currentframe] = delay * GAME_SPEED / 100;
5977 
5978     // Allocate body boxes.
5979     if((body_coords->width - body_coords->x)
5980         && (body_coords->height - body_coords->y))
5981     {
5982         // Set vulnerability.
5983         a->vulnerable[currentframe] = 1;
5984 
5985         // If there is no previous collision
5986         // list on this animation's frames,
5987         // we need to allocate space.
5988         if(!a->collision_body)
5989         {
5990             // Get memory size.
5991             size_col_on_frame = framecount * sizeof(*a->collision_body);
5992 
5993             // Allocate memory.
5994             a->collision_body = malloc(size_col_on_frame);
5995             memset(a->collision_body, 0, size_col_on_frame);
5996         }
5997 
5998         // Get memory size for the collision
5999         // list structure.
6000         size_col_on_frame_struct = sizeof(**a->collision_body);
6001 
6002         // Allocate memory for collision list
6003         // structure and store resulting pointer
6004         // on current frame.
6005         a->collision_body[currentframe] = malloc(size_col_on_frame_struct);
6006 
6007         // Allocate memory for a pointer to each instance
6008         // and set empty default.
6009         a->collision_body[currentframe]->instance = collision_alloc_body_list();
6010 
6011         // Loop instances, allocate memory, and assign
6012         // user values.
6013         for(i=0; i<max_collisons; i++)
6014         {
6015             // Allocate memory for this instance structure.
6016             // We're also using a local pointer for readability.
6017             collision_body = collision_alloc_body_instance(bbox);
6018             a->collision_body[currentframe]->instance[i] = collision_body;
6019 
6020             // Set index. Engine does not need this,
6021             // but will allow scripts to identify
6022             // which item instance has been acquired
6023             // by handle (pointer).
6024             collision_body->index = i;
6025 
6026             // Now let's allocate sub-properties.
6027 
6028             // Coordinates.
6029             if(!collision_body->coords)
6030             {
6031                 collision_body->coords = collision_alloc_coords(body_coords);
6032             }
6033         }
6034     }
6035 
6036     // Allocate entity boxes.
6037     if((entity_coords->width - entity_coords->x)
6038         && (entity_coords->height - entity_coords->y))
6039     {
6040         if(!a->collision_entity)
6041         {
6042             size_col_on_frame = framecount * sizeof(*a->collision_entity);
6043 
6044             a->collision_entity = malloc(size_col_on_frame);
6045             memset(a->collision_entity, 0, size_col_on_frame);
6046         }
6047 
6048         size_col_on_frame_struct = sizeof(**a->collision_entity);
6049         a->collision_entity[currentframe] = malloc(size_col_on_frame_struct);
6050 
6051         a->collision_entity[currentframe]->instance = collision_alloc_entity_list();
6052 
6053         for(i=0; i<max_collisons; i++)
6054         {
6055             collision_entity = collision_alloc_entity_instance(ebox);
6056             a->collision_entity[currentframe]->instance[i] = collision_entity;
6057 
6058             collision_entity->index = i;
6059 
6060             // Coordinates.
6061             if(!collision_entity->coords)
6062             {
6063                 collision_entity->coords = collision_alloc_coords(entity_coords);
6064             }
6065         }
6066     }
6067 
6068     // Allocate attack boxes. See body box
6069     // for notes.
6070     if((attack_coords->width - attack_coords->x) &&
6071             (attack_coords->height - attack_coords->y))
6072     {
6073         if(!a->collision_attack)
6074         {
6075             size_col_on_frame = framecount * sizeof(*a->collision_attack);
6076 
6077             a->collision_attack = malloc(size_col_on_frame);
6078             memset(a->collision_attack, 0, size_col_on_frame);
6079         }
6080 
6081         size_col_on_frame_struct = sizeof(**a->collision_attack);
6082         a->collision_attack[currentframe] = malloc(size_col_on_frame_struct);
6083 
6084         a->collision_attack[currentframe]->instance = collision_alloc_attack_list();
6085 
6086         for(i=0; i<max_collisons; i++)
6087         {
6088             collision_attack = collision_alloc_attack_instance(attack);
6089             a->collision_attack[currentframe]->instance[i] = collision_attack;
6090 
6091             collision_attack->index = i;
6092 
6093             // Coordinates.
6094             if(!collision_attack->coords)
6095             {
6096                 collision_attack->coords = collision_alloc_coords(attack_coords);
6097             }
6098 
6099             // Recursive damage.
6100             if(!collision_attack->recursive && recursive->mode)
6101             {
6102                 collision_attack->recursive = malloc(sizeof(*recursive));
6103                 memcpy(collision_attack->recursive, recursive, sizeof(*recursive));
6104             }
6105         }
6106     }
6107 
6108     if(drawmethod->flag)
6109     {
6110         if(!a->drawmethods)
6111         {
6112             a->drawmethods = malloc(framecount * sizeof(*a->drawmethods));
6113             memset(a->drawmethods, 0, framecount * sizeof(*a->drawmethods));
6114         }
6115         setDrawMethod(a, currentframe, malloc(sizeof(**a->drawmethods)));
6116         //a->drawmethods[currenframe] = malloc(sizeof(s_drawmethod));
6117         memcpy(getDrawMethod(a, currentframe), drawmethod, sizeof(**a->drawmethods));
6118         //memcpy(a->drawmethods[currentframe], drawmethod, sizeof(s_drawmethod));
6119     }
6120     if(idle && !a->idle)
6121     {
6122         a->idle = malloc(framecount * sizeof(*a->idle));
6123         memset(a->idle, 0, framecount * sizeof(*a->idle));
6124     }
6125     if(a->idle)
6126     {
6127         a->idle[currentframe] = idle;
6128     }
6129 
6130     if(move)
6131     {
6132         if(!a->move)
6133         {
6134             a->move = malloc(framecount * sizeof(*a->move));
6135             memset(a->move, 0, framecount * sizeof(*a->move));
6136         }
6137         a->move[currentframe] = malloc(sizeof(**a->move));
6138         memcpy(a->move[currentframe], move, sizeof(**a->move));
6139     }
6140 
6141     if(frameshadow >= 0 && !a->shadow)
6142     {
6143         a->shadow = malloc(framecount * sizeof(*a->shadow));
6144         memset(a->shadow, -1, framecount * sizeof(*a->shadow)); //default to -1
6145     }
6146     if(a->shadow)
6147     {
6148         a->shadow[currentframe] = frameshadow;    // shadow index for each frame
6149     }
6150     if(shadow_coords[0] || shadow_coords[1])
6151     {
6152         if(!a->shadow_coords)
6153         {
6154             a->shadow_coords = malloc(framecount * sizeof(*a->shadow_coords));
6155             memset(a->shadow_coords, 0, framecount * sizeof(*a->shadow_coords));
6156         }
6157         memcpy(a->shadow_coords[currentframe], shadow_coords, sizeof(*a->shadow_coords));
6158     }
6159 
6160     // Offset
6161     if(offset->x || offset->y)
6162     {
6163         if(!a->offset)
6164         {
6165             a->offset = malloc(framecount * sizeof(*a->offset));
6166             memset(a->offset, 0, framecount * sizeof(*a->offset));
6167         }
6168         a->offset[currentframe] = malloc(sizeof(**a->offset));
6169         memcpy(a->offset[currentframe], offset, sizeof(**a->offset));
6170     }
6171 
6172     if(platform[PLATFORM_HEIGHT]) //height
6173     {
6174         if(!a->platform)
6175         {
6176             a->platform = malloc(framecount * sizeof(*a->platform));
6177             memset(a->platform, 0, framecount * sizeof(*a->platform));
6178         }
6179         memcpy(a->platform[currentframe], platform, sizeof(*a->platform));// Used so entity can be landed on
6180     }
6181     if(soundtoplay >= 0)
6182     {
6183         if(!a->soundtoplay)
6184         {
6185             a->soundtoplay = malloc(framecount * sizeof(*a->soundtoplay));
6186             memset(a->soundtoplay, -1, framecount * sizeof(*a->soundtoplay)); // default to -1
6187         }
6188         a->soundtoplay[currentframe] = soundtoplay;
6189     }
6190 
6191     return a->numframes;
6192 }
6193 
6194 
6195 // ok this func only seems to overwrite the name which was assigned from models.txt with the one
6196 // in the models own text file.
6197 // it does so in the cache.
_peek_model_name(int index)6198 void _peek_model_name(int index)
6199 {
6200     size_t size = 0;
6201     ptrdiff_t pos = 0, len;
6202     char *buf = NULL;
6203     char *command, *value;
6204     ArgList arglist;
6205     char argbuf[MAX_ARG_LEN + 1] = "";
6206     modelCommands cmd;
6207 
6208     if(buffer_pakfile(model_cache[index].path, &buf, &size) != 1)
6209     {
6210         return;
6211     }
6212 
6213     while(pos < size)
6214     {
6215         ParseArgs(&arglist, buf + pos, argbuf);
6216         command = GET_ARG(0);
6217 
6218         if(command && command[0])
6219         {
6220             cmd = getModelCommand(modelcmdlist, command);
6221             if(cmd == CMD_MODEL_NAME)
6222             {
6223                 value = GET_ARG(1);
6224                 free(model_cache[index].name);
6225                 model_cache[index].name = NULL;
6226                 len = strlen(value);
6227                 model_cache[index].name = malloc(len + 1);
6228                 strcpy(model_cache[index].name, value);
6229                 model_cache[index].name[len] = 0;
6230                 break;
6231             }
6232         }
6233         pos += getNewLineStart(buf + pos);
6234     }
6235 
6236     if(buf != NULL)
6237     {
6238         free(buf);
6239         buf = NULL;
6240     }
6241 }
6242 
prepare_cache_map(size_t size)6243 void prepare_cache_map(size_t size)
6244 {
6245     if(model_cache == NULL || size + 1 > cache_map_max_items )
6246     {
6247 #ifdef VERBOSE
6248         printf("%s %p\n", "prepare_cache_map was", model_cache);
6249 #endif
6250         do
6251         {
6252             cache_map_max_items += 128;
6253         }
6254         while (size + 1 > cache_map_max_items);
6255 
6256         model_cache = realloc(model_cache, sizeof(*model_cache) * cache_map_max_items);
6257         if(model_cache == NULL)
6258         {
6259             borShutdown(1, "Out Of Memory!  Failed to create a new cache_map\n");
6260         }
6261     }
6262 }
6263 
cache_model(char * name,char * path,int flag)6264 void cache_model(char *name, char *path, int flag)
6265 {
6266     int len;
6267     printf("Cacheing '%s' from %s\n", name, path);
6268     prepare_cache_map(models_cached + 1);
6269     memset(&model_cache[models_cached], 0, sizeof(model_cache[models_cached]));
6270 
6271     len = strlen(name);
6272     model_cache[models_cached].name = malloc(len + 1);
6273     strcpy(model_cache[models_cached].name, name);
6274     model_cache[models_cached].name[len] = 0;
6275 
6276     len = strlen(path);
6277     model_cache[models_cached].path = malloc(len + 1);
6278     strcpy(model_cache[models_cached].path, path);
6279     model_cache[models_cached].path[len] = 0;
6280 
6281     model_cache[models_cached].loadflag = flag;
6282 
6283     _peek_model_name(models_cached);
6284     ++models_cached;
6285 }
6286 
6287 
free_modelcache()6288 void free_modelcache()
6289 {
6290     if(model_cache != NULL)
6291     {
6292         while(models_cached)
6293         {
6294             --models_cached;
6295             free(model_cache[models_cached].name);
6296             model_cache[models_cached].name = NULL;
6297             free(model_cache[models_cached].path);
6298             model_cache[models_cached].path = NULL;
6299         }
6300         free(model_cache);
6301         model_cache = NULL;
6302     }
6303 }
6304 
6305 
get_cached_model_index(char * name)6306 int get_cached_model_index(char *name)
6307 {
6308     int i;
6309     for(i = 0; i < models_cached; i++)
6310     {
6311         if(stricmp(name, model_cache[i].name) == 0)
6312         {
6313             return i;
6314         }
6315     }
6316     return -1;
6317 }
6318 
get_cached_model_path(char * name)6319 char *get_cached_model_path(char *name)
6320 {
6321     int i;
6322     for(i = 0; i < models_cached; i++)
6323     {
6324         if(stricmp(name, model_cache[i].name) == 0)
6325         {
6326             return model_cache[i].path;
6327         }
6328     }
6329     return NULL;
6330 }
6331 
6332 static void _readbarstatus(char *, s_barstatus *);
6333 
translate_attack_type(char * command)6334 static int translate_attack_type(char *command)
6335 {
6336     int atk_id = -1, tempInt;
6337 
6338     modelCommands cmd = getModelCommand(modelcmdlist, command);
6339 
6340     switch(cmd)
6341     {
6342     case CMD_MODEL_COLLISION:
6343     case CMD_MODEL_COLLISION1:
6344         atk_id = ATK_NORMAL;
6345         break;
6346     case CMD_MODEL_COLLISION2:
6347         atk_id   = ATK_NORMAL2;
6348         break;
6349     case CMD_MODEL_COLLISION3:
6350         atk_id  = ATK_NORMAL3;
6351         break;
6352     case CMD_MODEL_COLLISION4:
6353         atk_id  = ATK_NORMAL4;
6354         break;
6355     case CMD_MODEL_COLLISION5:
6356         atk_id  = ATK_NORMAL5;
6357         break;
6358     case CMD_MODEL_COLLISION6:
6359         atk_id  = ATK_NORMAL6;
6360         break;
6361     case CMD_MODEL_COLLISION7:
6362         atk_id  = ATK_NORMAL7;
6363         break;
6364     case CMD_MODEL_COLLISION8:
6365         atk_id  = ATK_NORMAL8;
6366         break;
6367     case CMD_MODEL_COLLISION9:
6368         atk_id  = ATK_NORMAL9;
6369         break;
6370     case CMD_MODEL_COLLISION10:
6371         atk_id  = ATK_NORMAL10;
6372         break;
6373     case CMD_MODEL_SHOCK:
6374         atk_id  = ATK_SHOCK;
6375         break;
6376     case CMD_MODEL_BURN:
6377         atk_id  = ATK_BURN;
6378         break;
6379     case CMD_MODEL_STEAL:
6380         atk_id  = ATK_STEAL;
6381         break;
6382     case CMD_MODEL_FREEZE:
6383         atk_id  = ATK_FREEZE;
6384         break;
6385     case CMD_MODEL_ITEMBOX:
6386         atk_id  = ATK_ITEM;
6387         break;
6388     case CMD_MODEL_LOSE:
6389         atk_id  = ATK_LOSE;
6390         break;
6391     case CMD_MODEL_COLLISION_ETC:
6392         tempInt = atoi(command + 6); // White Dragon: 6 is "ATTACK" string length
6393         if(tempInt < MAX_ATKS - STA_ATKS + 1)
6394         {
6395             tempInt = MAX_ATKS - STA_ATKS + 1;
6396         }
6397         atk_id = tempInt + STA_ATKS - 1;
6398         break;
6399     default:
6400         break;
6401     }
6402 
6403     return atk_id;
6404 }
6405 
6406 //move here to ease animation name to id logic
translate_ani_id(const char * value,s_model * newchar,s_anim * newanim,s_collision_attack * attack)6407 static int translate_ani_id(const char *value, s_model *newchar, s_anim *newanim, s_collision_attack *attack)
6408 {
6409     int ani_id = -1, tempInt;
6410     //those are dummy values to simplify code
6411     static s_model mdl;
6412     static s_anim ani;
6413     static s_collision_attack atk;
6414     if(!newchar)
6415     {
6416         newchar = &mdl;
6417     }
6418     if(!newanim)
6419     {
6420         newanim = &ani;
6421     }
6422     if(!attack)
6423     {
6424         attack = &atk;
6425     }
6426 
6427     if(starts_with_num(value, "idle"))
6428     {
6429         get_tail_number(tempInt, value, "idle");
6430         ani_id = animidles[tempInt - 1];
6431     }
6432     else if(stricmp(value, "waiting") == 0)
6433     {
6434         ani_id = ANI_SELECT;
6435     }
6436 	else if (stricmp(value, "selectin") == 0)
6437 	{
6438 		ani_id = ANI_SELECTIN;
6439 	}
6440 	else if (stricmp(value, "selectout") == 0)
6441 	{
6442 		ani_id = ANI_SELECTOUT;
6443 	}
6444     else if(starts_with_num(value, "walk"))
6445     {
6446         get_tail_number(tempInt, value, "walk");
6447         ani_id = animwalks[tempInt - 1];
6448         newanim->sync = ANI_WALK;
6449 
6450     }
6451     else if(stricmp(value, "sleep") == 0)
6452     {
6453         ani_id = ANI_SLEEP;
6454     }
6455     else if(stricmp(value, "run") == 0)
6456     {
6457         ani_id = ANI_RUN;
6458     }
6459     else if(stricmp(value, "backrun") == 0)
6460     {
6461         ani_id = ANI_BACKRUN;
6462     }
6463     else if(starts_with_num(value, "up"))
6464     {
6465         get_tail_number(tempInt, value, "up");
6466         ani_id = animups[tempInt - 1];
6467         newanim->sync = ANI_WALK;
6468     }
6469     else if(starts_with_num(value, "down"))
6470     {
6471         get_tail_number(tempInt, value, "down");
6472         ani_id = animdowns[tempInt - 1];
6473         newanim->sync = ANI_WALK;
6474     }
6475     else if(starts_with_num(value, "backwalk"))
6476     {
6477         get_tail_number(tempInt, value, "backwalk");
6478         ani_id = animbackwalks[tempInt - 1];
6479         newanim->sync = ANI_WALK;
6480     }
6481     else if(stricmp(value, "jump") == 0)
6482     {
6483         ani_id = ANI_JUMP;
6484         newanim->range.x.min = 50;
6485         newanim->range.x.max = 60;
6486     }
6487     else if(stricmp(value, "duck") == 0)
6488     {
6489         ani_id = ANI_DUCK;
6490     }
6491     else if(stricmp(value, "land") == 0)
6492     {
6493         ani_id = ANI_LAND;
6494     }
6495     else if(starts_with_num(value, "pain"))
6496     {
6497         get_tail_number(tempInt, value, "pain");
6498         if(tempInt == 1)
6499         {
6500             ani_id = ANI_PAIN;
6501         }
6502         else if(tempInt == 2)
6503         {
6504             ani_id = ANI_PAIN2;
6505         }
6506         else if(tempInt == 3)
6507         {
6508             ani_id = ANI_PAIN3;
6509         }
6510         else if(tempInt == 4)
6511         {
6512             ani_id = ANI_PAIN4;
6513         }
6514         else if(tempInt == 5)
6515         {
6516             ani_id = ANI_PAIN5;
6517         }
6518         else if(tempInt == 6)
6519         {
6520             ani_id = ANI_PAIN6;
6521         }
6522         else if(tempInt == 7)
6523         {
6524             ani_id = ANI_PAIN7;
6525         }
6526         else if(tempInt == 8)
6527         {
6528             ani_id = ANI_PAIN8;
6529         }
6530         else if(tempInt == 9)
6531         {
6532             ani_id = ANI_PAIN9;
6533         }
6534         else if(tempInt == 10)
6535         {
6536             ani_id = ANI_PAIN10;
6537         }
6538         else
6539         {
6540             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6541             {
6542                 tempInt = MAX_ATKS - STA_ATKS + 1;
6543             }
6544             ani_id = animpains[tempInt + STA_ATKS - 1];
6545         }
6546     }
6547     else if(starts_with_num(value, "backpain"))
6548     {
6549         get_tail_number(tempInt, value, "backpain");
6550         if(tempInt == 1)
6551         {
6552             ani_id = ANI_BACKPAIN;
6553         }
6554         else if(tempInt == 2)
6555         {
6556             ani_id = ANI_BACKPAIN2;
6557         }
6558         else if(tempInt == 3)
6559         {
6560             ani_id = ANI_BACKPAIN3;
6561         }
6562         else if(tempInt == 4)
6563         {
6564             ani_id = ANI_BACKPAIN4;
6565         }
6566         else if(tempInt == 5)
6567         {
6568             ani_id = ANI_BACKPAIN5;
6569         }
6570         else if(tempInt == 6)
6571         {
6572             ani_id = ANI_BACKPAIN6;
6573         }
6574         else if(tempInt == 7)
6575         {
6576             ani_id = ANI_BACKPAIN7;
6577         }
6578         else if(tempInt == 8)
6579         {
6580             ani_id = ANI_BACKPAIN8;
6581         }
6582         else if(tempInt == 9)
6583         {
6584             ani_id = ANI_BACKPAIN9;
6585         }
6586         else if(tempInt == 10)
6587         {
6588             ani_id = ANI_BACKPAIN10;
6589         }
6590         else
6591         {
6592             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6593             {
6594                 tempInt = MAX_ATKS - STA_ATKS + 1;
6595             }
6596             ani_id = animbackpains[tempInt + STA_ATKS - 1];
6597         }
6598     }
6599     else if(stricmp(value, "spain") == 0)   // If shock attacks don't knock opponent down, play this
6600     {
6601         ani_id = ANI_SHOCKPAIN;
6602     }
6603     else if(stricmp(value, "bpain") == 0)   // If burn attacks don't knock opponent down, play this
6604     {
6605         ani_id = ANI_BURNPAIN;
6606     }
6607     else if(stricmp(value, "backspain") == 0)   // If shock attacks don't knock opponent down, play this
6608     {
6609         ani_id = ANI_BACKSHOCKPAIN;
6610     }
6611     else if(stricmp(value, "backbpain") == 0)   // If burn attacks don't knock opponent down, play this
6612     {
6613         ani_id = ANI_BACKBURNPAIN;
6614     }
6615     else if(starts_with_num(value, "fall"))
6616     {
6617         get_tail_number(tempInt, value, "fall");
6618         if(tempInt == 1)
6619         {
6620             ani_id = ANI_FALL;
6621         }
6622         else if(tempInt == 2)
6623         {
6624             ani_id = ANI_FALL2;
6625         }
6626         else if(tempInt == 3)
6627         {
6628             ani_id = ANI_FALL3;
6629         }
6630         else if(tempInt == 4)
6631         {
6632             ani_id = ANI_FALL4;
6633         }
6634         else if(tempInt == 5)
6635         {
6636             ani_id = ANI_FALL5;
6637         }
6638         else if(tempInt == 6)
6639         {
6640             ani_id = ANI_FALL6;
6641         }
6642         else if(tempInt == 7)
6643         {
6644             ani_id = ANI_FALL7;
6645         }
6646         else if(tempInt == 8)
6647         {
6648             ani_id = ANI_FALL8;
6649         }
6650         else if(tempInt == 9)
6651         {
6652             ani_id = ANI_FALL9;
6653         }
6654         else if(tempInt == 10)
6655         {
6656             ani_id = ANI_FALL10;
6657         }
6658         else
6659         {
6660             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6661             {
6662                 tempInt = MAX_ATKS - STA_ATKS + 1;
6663             }
6664             ani_id = animfalls[tempInt + STA_ATKS - 1];
6665         }
6666         newanim->bounce = 4;
6667     }
6668     else if(starts_with_num(value, "backfall"))
6669     {
6670         get_tail_number(tempInt, value, "backfall");
6671         if(tempInt == 1)
6672         {
6673             ani_id = ANI_BACKFALL;
6674         }
6675         else if(tempInt == 2)
6676         {
6677             ani_id = ANI_BACKFALL2;
6678         }
6679         else if(tempInt == 3)
6680         {
6681             ani_id = ANI_BACKFALL3;
6682         }
6683         else if(tempInt == 4)
6684         {
6685             ani_id = ANI_BACKFALL4;
6686         }
6687         else if(tempInt == 5)
6688         {
6689             ani_id = ANI_BACKFALL5;
6690         }
6691         else if(tempInt == 6)
6692         {
6693             ani_id = ANI_BACKFALL6;
6694         }
6695         else if(tempInt == 7)
6696         {
6697             ani_id = ANI_BACKFALL7;
6698         }
6699         else if(tempInt == 8)
6700         {
6701             ani_id = ANI_BACKFALL8;
6702         }
6703         else if(tempInt == 9)
6704         {
6705             ani_id = ANI_BACKFALL9;
6706         }
6707         else if(tempInt == 10)
6708         {
6709             ani_id = ANI_BACKFALL10;
6710         }
6711         else
6712         {
6713             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6714             {
6715                 tempInt = MAX_ATKS - STA_ATKS + 1;
6716             }
6717             ani_id = animbackfalls[tempInt + STA_ATKS - 1];
6718         }
6719         newanim->bounce = 4;
6720     }
6721     else if(stricmp(value, "shock") == 0)   // If shock attacks do knock opponent down, play this
6722     {
6723         ani_id = ANI_SHOCK;
6724         newanim->bounce = 4;
6725     }
6726     else if(stricmp(value, "backshock") == 0)   // If shock attacks do knock opponent down, play this
6727     {
6728         ani_id = ANI_BACKSHOCK;
6729         newanim->bounce = 4;
6730     }
6731     else if(stricmp(value, "burn") == 0)   // If burn attacks do knock opponent down, play this
6732     {
6733         ani_id = ANI_BURN;
6734         newanim->bounce = 4;
6735     }
6736     else if(stricmp(value, "backburn") == 0)   // If burn attacks do knock opponent down, play this
6737     {
6738         ani_id = ANI_BACKBURN;
6739         newanim->bounce = 4;
6740     }
6741     else if(starts_with_num(value, "death"))
6742     {
6743         get_tail_number(tempInt, value, "death");
6744         if(tempInt == 1)
6745         {
6746             ani_id = ANI_DIE;
6747         }
6748         else if(tempInt == 2)
6749         {
6750             ani_id = ANI_DIE2;
6751         }
6752         else if(tempInt == 3)
6753         {
6754             ani_id = ANI_DIE3;
6755         }
6756         else if(tempInt == 4)
6757         {
6758             ani_id = ANI_DIE4;
6759         }
6760         else if(tempInt == 5)
6761         {
6762             ani_id = ANI_DIE5;
6763         }
6764         else if(tempInt == 6)
6765         {
6766             ani_id = ANI_DIE6;
6767         }
6768         else if(tempInt == 7)
6769         {
6770             ani_id = ANI_DIE7;
6771         }
6772         else if(tempInt == 8)
6773         {
6774             ani_id = ANI_DIE8;
6775         }
6776         else if(tempInt == 9)
6777         {
6778             ani_id = ANI_DIE9;
6779         }
6780         else if(tempInt == 10)
6781         {
6782             ani_id = ANI_DIE10;
6783         }
6784         else
6785         {
6786             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6787             {
6788                 tempInt = MAX_ATKS - STA_ATKS + 1;
6789             }
6790             ani_id = animdies[tempInt + STA_ATKS - 1];
6791         }
6792     }
6793     else if(starts_with_num(value, "backdeath"))
6794     {
6795         get_tail_number(tempInt, value, "backdeath");
6796         if(tempInt == 1)
6797         {
6798             ani_id = ANI_BACKDIE;
6799         }
6800         else if(tempInt == 2)
6801         {
6802             ani_id = ANI_BACKDIE2;
6803         }
6804         else if(tempInt == 3)
6805         {
6806             ani_id = ANI_BACKDIE3;
6807         }
6808         else if(tempInt == 4)
6809         {
6810             ani_id = ANI_BACKDIE4;
6811         }
6812         else if(tempInt == 5)
6813         {
6814             ani_id = ANI_BACKDIE5;
6815         }
6816         else if(tempInt == 6)
6817         {
6818             ani_id = ANI_BACKDIE6;
6819         }
6820         else if(tempInt == 7)
6821         {
6822             ani_id = ANI_BACKDIE7;
6823         }
6824         else if(tempInt == 8)
6825         {
6826             ani_id = ANI_BACKDIE8;
6827         }
6828         else if(tempInt == 9)
6829         {
6830             ani_id = ANI_BACKDIE9;
6831         }
6832         else if(tempInt == 10)
6833         {
6834             ani_id = ANI_BACKDIE10;
6835         }
6836         else
6837         {
6838             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6839             {
6840                 tempInt = MAX_ATKS - STA_ATKS + 1;
6841             }
6842             ani_id = animbackdies[tempInt + STA_ATKS - 1];
6843         }
6844     }
6845     else if(stricmp(value, "sdie") == 0)
6846     {
6847         ani_id = ANI_SHOCKDIE;
6848     }
6849     else if(stricmp(value, "bdie") == 0)
6850     {
6851         ani_id = ANI_BURNDIE;
6852     }
6853     else if(stricmp(value, "backsdie") == 0)
6854     {
6855         ani_id = ANI_BACKSHOCKDIE;
6856     }
6857     else if(stricmp(value, "backbdie") == 0)
6858     {
6859         ani_id = ANI_BACKBURNDIE;
6860     }
6861     else if(stricmp(value, "chipdeath") == 0)
6862     {
6863         ani_id = ANI_CHIPDEATH;
6864     }
6865     else if(stricmp(value, "guardbreak") == 0)
6866     {
6867         ani_id = ANI_GUARDBREAK;
6868     }
6869     else if(stricmp(value, "riseb") == 0)
6870     {
6871         ani_id = ANI_RISEB;
6872     }
6873     else if(stricmp(value, "backriseb") == 0)
6874     {
6875         ani_id = ANI_BACKRISEB;
6876     }
6877     else if(stricmp(value, "rises") == 0)
6878     {
6879         ani_id = ANI_RISES;
6880     }
6881     else if(stricmp(value, "backrises") == 0)
6882     {
6883         ani_id = ANI_BACKRISES;
6884     }
6885     else if(starts_with_num(value, "rise"))
6886     {
6887         get_tail_number(tempInt, value, "rise");
6888         if(tempInt == 1)
6889         {
6890             ani_id = ANI_RISE;
6891         }
6892         else if(tempInt == 2)
6893         {
6894             ani_id = ANI_RISE2;
6895         }
6896         else if(tempInt == 3)
6897         {
6898             ani_id = ANI_RISE3;
6899         }
6900         else if(tempInt == 4)
6901         {
6902             ani_id = ANI_RISE4;
6903         }
6904         else if(tempInt == 5)
6905         {
6906             ani_id = ANI_RISE5;
6907         }
6908         else if(tempInt == 6)
6909         {
6910             ani_id = ANI_RISE6;
6911         }
6912         else if(tempInt == 7)
6913         {
6914             ani_id = ANI_RISE7;
6915         }
6916         else if(tempInt == 8)
6917         {
6918             ani_id = ANI_RISE8;
6919         }
6920         else if(tempInt == 9)
6921         {
6922             ani_id = ANI_RISE9;
6923         }
6924         else if(tempInt == 10)
6925         {
6926             ani_id = ANI_RISE10;
6927         }
6928         else
6929         {
6930             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6931             {
6932                 tempInt = MAX_ATKS - STA_ATKS + 1;
6933             }
6934             ani_id = animrises[tempInt + STA_ATKS - 1];
6935         }
6936     }
6937     else if(starts_with_num(value, "backrise"))
6938     {
6939         get_tail_number(tempInt, value, "backrise");
6940         if(tempInt == 1)
6941         {
6942             ani_id = ANI_BACKRISE;
6943         }
6944         else if(tempInt == 2)
6945         {
6946             ani_id = ANI_BACKRISE2;
6947         }
6948         else if(tempInt == 3)
6949         {
6950             ani_id = ANI_BACKRISE3;
6951         }
6952         else if(tempInt == 4)
6953         {
6954             ani_id = ANI_BACKRISE4;
6955         }
6956         else if(tempInt == 5)
6957         {
6958             ani_id = ANI_BACKRISE5;
6959         }
6960         else if(tempInt == 6)
6961         {
6962             ani_id = ANI_BACKRISE6;
6963         }
6964         else if(tempInt == 7)
6965         {
6966             ani_id = ANI_BACKRISE7;
6967         }
6968         else if(tempInt == 8)
6969         {
6970             ani_id = ANI_BACKRISE8;
6971         }
6972         else if(tempInt == 9)
6973         {
6974             ani_id = ANI_BACKRISE9;
6975         }
6976         else if(tempInt == 10)
6977         {
6978             ani_id = ANI_BACKRISE10;
6979         }
6980         else
6981         {
6982             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6983             {
6984                 tempInt = MAX_ATKS - STA_ATKS + 1;
6985             }
6986             ani_id = animbackrises[tempInt + STA_ATKS - 1];
6987         }
6988     }
6989     else if(stricmp(value, "riseattackb") == 0)
6990     {
6991         ani_id = ANI_RISEATTACKB;
6992     }
6993     else if(stricmp(value, "backriseattackb") == 0)
6994     {
6995         ani_id = ANI_BACKRISEATTACKB;
6996     }
6997     else if(stricmp(value, "riseattacks") == 0)
6998     {
6999         ani_id = ANI_RISEATTACKS;
7000     }
7001     else if(stricmp(value, "backriseattacks") == 0)
7002     {
7003         ani_id = ANI_BACKRISEATTACKS;
7004     }
7005     else if(starts_with_num(value, "riseattack"))
7006     {
7007         get_tail_number(tempInt, value, "riseattack");
7008         if(tempInt == 1)
7009         {
7010             ani_id = ANI_RISEATTACK;
7011         }
7012         else if(tempInt == 2)
7013         {
7014             ani_id = ANI_RISEATTACK2;
7015         }
7016         else if(tempInt == 3)
7017         {
7018             ani_id = ANI_RISEATTACK3;
7019         }
7020         else if(tempInt == 4)
7021         {
7022             ani_id = ANI_RISEATTACK4;
7023         }
7024         else if(tempInt == 6)
7025         {
7026             ani_id = ANI_RISEATTACK5;
7027         }
7028         else if(tempInt == 6)
7029         {
7030             ani_id = ANI_RISEATTACK6;
7031         }
7032         else if(tempInt == 7)
7033         {
7034             ani_id = ANI_RISEATTACK7;
7035         }
7036         else if(tempInt == 8)
7037         {
7038             ani_id = ANI_RISEATTACK8;
7039         }
7040         else if(tempInt == 9)
7041         {
7042             ani_id = ANI_RISEATTACK9;
7043         }
7044         else if(tempInt == 10)
7045         {
7046             ani_id = ANI_RISEATTACK10;
7047         }
7048         else
7049         {
7050             if(tempInt < MAX_ATKS - STA_ATKS + 1)
7051             {
7052                 tempInt = MAX_ATKS - STA_ATKS + 1;
7053             }
7054             ani_id = animriseattacks[tempInt + STA_ATKS - 1];
7055         }
7056     }
7057     else if(starts_with_num(value, "backriseattack"))
7058     {
7059         get_tail_number(tempInt, value, "backriseattack");
7060         if(tempInt == 1)
7061         {
7062             ani_id = ANI_BACKRISEATTACK;
7063         }
7064         else if(tempInt == 2)
7065         {
7066             ani_id = ANI_BACKRISEATTACK2;
7067         }
7068         else if(tempInt == 3)
7069         {
7070             ani_id = ANI_BACKRISEATTACK3;
7071         }
7072         else if(tempInt == 4)
7073         {
7074             ani_id = ANI_BACKRISEATTACK4;
7075         }
7076         else if(tempInt == 6)
7077         {
7078             ani_id = ANI_BACKRISEATTACK5;
7079         }
7080         else if(tempInt == 6)
7081         {
7082             ani_id = ANI_BACKRISEATTACK6;
7083         }
7084         else if(tempInt == 7)
7085         {
7086             ani_id = ANI_BACKRISEATTACK7;
7087         }
7088         else if(tempInt == 8)
7089         {
7090             ani_id = ANI_BACKRISEATTACK8;
7091         }
7092         else if(tempInt == 9)
7093         {
7094             ani_id = ANI_BACKRISEATTACK9;
7095         }
7096         else if(tempInt == 10)
7097         {
7098             ani_id = ANI_BACKRISEATTACK10;
7099         }
7100         else
7101         {
7102             if(tempInt < MAX_ATKS - STA_ATKS + 1)
7103             {
7104                 tempInt = MAX_ATKS - STA_ATKS + 1;
7105             }
7106             ani_id = animbackriseattacks[tempInt + STA_ATKS - 1];
7107         }
7108     }
7109     else if(stricmp(value, "select") == 0)
7110     {
7111         ani_id = ANI_PICK;
7112     }
7113     else if(starts_with_num(value, "attack"))
7114     {
7115         get_tail_number(tempInt, value, "attack");
7116         ani_id = animattacks[tempInt - 1];
7117     }
7118     else if(stricmp(value, "throwattack") == 0)
7119     {
7120         ani_id = ANI_THROWATTACK;
7121     }
7122     else if(stricmp(value, "upper") == 0)
7123     {
7124         ani_id = ANI_UPPER;
7125         attack->counterattack = 100; //default to 100
7126         newanim->range.x.min = -10;
7127         newanim->range.x.max = 120;
7128     }
7129     else if(stricmp(value, "cant") == 0)
7130     {
7131         ani_id = ANI_CANT;
7132     }
7133     else if(stricmp(value, "jumpcant") == 0)
7134     {
7135         ani_id = ANI_JUMPCANT;
7136     }
7137     else if(stricmp(value, "charge") == 0)
7138     {
7139         ani_id = ANI_CHARGE;
7140     }
7141     else if(stricmp(value, "faint") == 0)
7142     {
7143         ani_id = ANI_FAINT;
7144     }
7145     else if(stricmp(value, "dodge") == 0)
7146     {
7147         ani_id = ANI_DODGE;
7148     }
7149     else if(stricmp(value, "special") == 0 || stricmp(value, "special1") == 0)
7150     {
7151         ani_id = ANI_SPECIAL;
7152         if(!newanim->energycost)
7153         {
7154             newanim->energycost         = malloc(sizeof(*newanim->energycost));
7155             memset(newanim->energycost, 0, sizeof(*newanim->energycost));
7156 
7157             newanim->energycost->cost   = ENERGYCOST_DEFAULT_COST;
7158             newanim->energycost->mponly = COST_TYPE_MP_THEN_HP;
7159         }
7160     }
7161     else if(stricmp(value, "special2") == 0)
7162     {
7163         ani_id = ANI_SPECIAL2;
7164         if(!newanim->energycost)
7165         {
7166             newanim->energycost         = malloc(sizeof(*newanim->energycost));
7167             memset(newanim->energycost, 0, sizeof(*newanim->energycost));
7168 
7169             newanim->energycost->cost   = ENERGYCOST_NOCOST;
7170             newanim->energycost->mponly = COST_TYPE_MP_THEN_HP;
7171         }
7172     }
7173     else if(stricmp(value, "special3") == 0 || stricmp(value, "jumpspecial") == 0)
7174     {
7175         ani_id = ANI_JUMPSPECIAL;
7176         if(!newanim->energycost)
7177         {
7178             newanim->energycost         = malloc(sizeof(*newanim->energycost));
7179             memset(newanim->energycost, 0, sizeof(*newanim->energycost));
7180 
7181             newanim->energycost->cost   = ENERGYCOST_NOCOST;
7182             newanim->energycost->mponly = COST_TYPE_MP_THEN_HP;
7183         }
7184     }
7185     else if(starts_with_num(value, "freespecial"))
7186     {
7187         get_tail_number(tempInt, value, "freespecial");
7188         ani_id = animspecials[tempInt - 1];
7189     }
7190     else if(stricmp(value, "jumpattack") == 0)
7191     {
7192         ani_id = ANI_JUMPATTACK;
7193         if(newchar->jumpheight == 4)
7194         {
7195             newanim->range.x.min = 150;
7196             newanim->range.x.max = 200;
7197         }
7198     }
7199     else if(stricmp(value, "jumpattack2") == 0)
7200     {
7201         ani_id = ANI_JUMPATTACK2;
7202     }
7203     else if(stricmp(value, "jumpattack3") == 0)
7204     {
7205         ani_id = ANI_JUMPATTACK3;
7206     }
7207     else if(stricmp(value, "jumpforward") == 0)
7208     {
7209         ani_id = ANI_JUMPFORWARD;
7210     }
7211     else if(stricmp(value, "runjumpattack") == 0)
7212     {
7213         ani_id = ANI_RUNJUMPATTACK;
7214     }
7215     else if(stricmp(value, "runattack") == 0)
7216     {
7217         ani_id = ANI_RUNATTACK;    // New attack for when a player is running
7218     }
7219     else if(stricmp(value, "attackup") == 0)
7220     {
7221         ani_id = ANI_ATTACKUP;    // New attack for when a player presses u u
7222     }
7223     else if(stricmp(value, "attackdown") == 0)
7224     {
7225         ani_id = ANI_ATTACKDOWN;    // New attack for when a player presses d d
7226     }
7227     else if(stricmp(value, "attackforward") == 0)
7228     {
7229         ani_id = ANI_ATTACKFORWARD;    // New attack for when a player presses f f
7230     }
7231     else if(stricmp(value, "attackbackward") == 0)
7232     {
7233         ani_id = ANI_ATTACKBACKWARD;    // New attack for when a player presses b a
7234     }
7235     else if(stricmp(value, "attackboth") == 0)   // Attack that is executed by holding down j and pressing a
7236     {
7237         ani_id = ANI_ATTACKBOTH;
7238     }
7239     else if(stricmp(value, "get") == 0)
7240     {
7241         ani_id = ANI_GET;
7242     }
7243     else if(stricmp(value, "grab") == 0)
7244     {
7245         ani_id = ANI_GRAB;
7246     }
7247     else if(stricmp(value, "grabwalk") == 0)
7248     {
7249         ani_id = ANI_GRABWALK;
7250         newanim->sync = ANI_GRABWALK;
7251     }
7252     else if(stricmp(value, "grabwalkup") == 0)
7253     {
7254         ani_id = ANI_GRABWALKUP;
7255         newanim->sync = ANI_GRABWALK;
7256     }
7257     else if(stricmp(value, "grabwalkdown") == 0)
7258     {
7259         ani_id = ANI_GRABWALKDOWN;
7260         newanim->sync = ANI_GRABWALK;
7261     }
7262     else if(stricmp(value, "grabbackwalk") == 0)
7263     {
7264         ani_id = ANI_GRABBACKWALK;
7265         newanim->sync = ANI_GRABWALK;
7266     }
7267     else if(stricmp(value, "grabturn") == 0)
7268     {
7269         ani_id = ANI_GRABTURN;
7270     }
7271     else if(stricmp(value, "grabbed") == 0)   // New grabbed animation for when grabbed
7272     {
7273         ani_id = ANI_GRABBED;
7274     }
7275     else if(stricmp(value, "grabbedwalk") == 0)   // New animation for when grabbed and forced to walk
7276     {
7277         ani_id = ANI_GRABBEDWALK;
7278         newanim->sync = ANI_GRABBEDWALK;
7279     }
7280     else if(stricmp(value, "grabbedwalkup") == 0)
7281     {
7282         ani_id = ANI_GRABWALKUP;
7283         newanim->sync = ANI_GRABBEDWALK;
7284     }
7285     else if(stricmp(value, "grabbedwalkdown") == 0)
7286     {
7287         ani_id = ANI_GRABWALKDOWN;
7288         newanim->sync = ANI_GRABBEDWALK;
7289     }
7290     else if(stricmp(value, "grabbedbackwalk") == 0)
7291     {
7292         ani_id = ANI_GRABBEDBACKWALK;
7293         newanim->sync = ANI_GRABBEDWALK;
7294     }
7295     else if(stricmp(value, "grabbedturn") == 0)
7296     {
7297         ani_id = ANI_GRABBEDTURN;
7298     }
7299     else if(stricmp(value, "grabattack") == 0)
7300     {
7301         ani_id = ANI_GRABATTACK;
7302         newanim->attackone = 1; // default to 1, attack one one opponent
7303     }
7304     else if(stricmp(value, "grabattack2") == 0)
7305     {
7306         ani_id = ANI_GRABATTACK2;
7307         newanim->attackone = 1;
7308     }
7309     else if(stricmp(value, "grabforward") == 0)   // New grab attack for when pressing forward attack
7310     {
7311         ani_id = ANI_GRABFORWARD;
7312         newanim->attackone = 1;
7313     }
7314     else if(stricmp(value, "grabforward2") == 0)   // New grab attack for when pressing forward attack
7315     {
7316         ani_id = ANI_GRABFORWARD2;
7317         newanim->attackone = 1;
7318     }
7319     else if(stricmp(value, "grabbackward") == 0)   // New grab attack for when pressing backward attack
7320     {
7321         ani_id = ANI_GRABBACKWARD;
7322         newanim->attackone = 1;
7323     }
7324     else if(stricmp(value, "grabbackward2") == 0)   // New grab attack for when pressing backward attack
7325     {
7326         ani_id = ANI_GRABBACKWARD2;
7327         newanim->attackone = 1;
7328     }
7329     else if(stricmp(value, "grabup") == 0)   // New grab attack for when pressing up attack
7330     {
7331         ani_id = ANI_GRABUP;
7332         newanim->attackone = 1;
7333     }
7334     else if(stricmp(value, "grabup2") == 0)   // New grab attack for when pressing up attack
7335     {
7336         ani_id = ANI_GRABUP2;
7337         newanim->attackone = 1;
7338     }
7339     else if(stricmp(value, "grabdown") == 0)   // New grab attack for when pressing down attack
7340     {
7341         ani_id = ANI_GRABDOWN;
7342         newanim->attackone = 1;
7343     }
7344     else if(stricmp(value, "grabdown2") == 0)   // New grab attack for when pressing down attack
7345     {
7346         ani_id = ANI_GRABDOWN2;
7347         newanim->attackone = 1;
7348     }
7349     else if(stricmp(value, "spawn") == 0)     //  spawn/respawn works separately now
7350     {
7351         ani_id = ANI_SPAWN;
7352     }
7353     else if(stricmp(value, "respawn") == 0)     //  spawn/respawn works separately now
7354     {
7355         ani_id = ANI_RESPAWN;
7356     }
7357     else if(stricmp(value, "throw") == 0)
7358     {
7359         ani_id = ANI_THROW;
7360     }
7361     else if(stricmp(value, "block") == 0)   // Now enemies can block attacks on occasion
7362     {
7363         ani_id = ANI_BLOCK;
7364         newanim->range.x.min = 1;
7365         newanim->range.x.max = 100;
7366     }
7367 	else if (stricmp(value, "blockrelease") == 0)
7368 	{
7369 		ani_id = ANI_BLOCKRELEASE;
7370 	}
7371 	else if (stricmp(value, "blockstart") == 0)
7372 	{
7373 		ani_id = ANI_BLOCKSTART;
7374 	}
7375     else if(starts_with_num(value, "follow"))
7376     {
7377         get_tail_number(tempInt, value, "follow");
7378         ani_id = animfollows[tempInt - 1];
7379     }
7380     else if(stricmp(value, "chargeattack") == 0)
7381     {
7382         ani_id = ANI_CHARGEATTACK;
7383     }
7384     else if(stricmp(value, "turn") == 0)
7385     {
7386         ani_id = ANI_TURN;
7387     }
7388     else if(stricmp(value, "forwardjump") == 0)
7389     {
7390         ani_id = ANI_FORWARDJUMP;
7391     }
7392     else if(stricmp(value, "runjump") == 0)
7393     {
7394         ani_id = ANI_RUNJUMP;
7395     }
7396     else if(stricmp(value, "jumpland") == 0)
7397     {
7398         ani_id = ANI_JUMPLAND;
7399     }
7400     else if(stricmp(value, "jumpdelay") == 0)
7401     {
7402         ani_id = ANI_JUMPDELAY;
7403     }
7404     else if(stricmp(value, "hitobstacle") == 0)
7405     {
7406         ani_id = ANI_HITOBSTACLE;
7407     }
7408     else if(stricmp(value, "hitplatform") == 0)
7409     {
7410         ani_id = ANI_HITPLATFORM;
7411     }
7412     else if(stricmp(value, "hitwall") == 0)
7413     {
7414         ani_id = ANI_HITWALL;
7415     }
7416     else if(stricmp(value, "slide") == 0)
7417     {
7418         ani_id = ANI_SLIDE;
7419     }
7420     else if(stricmp(value, "runslide") == 0)
7421     {
7422         ani_id = ANI_RUNSLIDE;
7423     }
7424     else if(stricmp(value, "blockpainb") == 0)
7425     {
7426         ani_id = ANI_BLOCKPAINB;
7427     }
7428     else if(stricmp(value, "blockpains") == 0)
7429     {
7430         ani_id = ANI_BLOCKPAINS;
7431     }
7432     else if(starts_with_num(value, "blockpain"))
7433     {
7434         get_tail_number(tempInt, value, "blockpain");
7435         if(tempInt == 1)
7436         {
7437             ani_id = ANI_BLOCKPAIN;
7438         }
7439         else if(tempInt == 2)
7440         {
7441             ani_id = ANI_BLOCKPAIN2;
7442         }
7443         else if(tempInt == 3)
7444         {
7445             ani_id = ANI_BLOCKPAIN3;
7446         }
7447         else if(tempInt == 4)
7448         {
7449             ani_id = ANI_BLOCKPAIN4;
7450         }
7451         else if(tempInt == 5)
7452         {
7453             ani_id = ANI_BLOCKPAIN5;
7454         }
7455         else if(tempInt == 6)
7456         {
7457             ani_id = ANI_BLOCKPAIN6;
7458         }
7459         else if(tempInt == 7)
7460         {
7461             ani_id = ANI_BLOCKPAIN7;
7462         }
7463         else if(tempInt == 8)
7464         {
7465             ani_id = ANI_BLOCKPAIN8;
7466         }
7467         else if(tempInt == 9)
7468         {
7469             ani_id = ANI_BLOCKPAIN9;
7470         }
7471         else if(tempInt == 10)
7472         {
7473             ani_id = ANI_BLOCKPAIN10;
7474         }
7475         else
7476         {
7477             if(tempInt < MAX_ATKS - STA_ATKS + 1)
7478             {
7479                 tempInt = MAX_ATKS - STA_ATKS + 1;
7480             }
7481             ani_id = animblkpains[tempInt + STA_ATKS - 1];
7482         }
7483     }
7484     else if(stricmp(value, "backblockpainb") == 0)
7485     {
7486         ani_id = ANI_BACKBLOCKPAINB;
7487     }
7488     else if(stricmp(value, "backblockpains") == 0)
7489     {
7490         ani_id = ANI_BACKBLOCKPAINS;
7491     }
7492     else if(starts_with_num(value, "backblockpain"))
7493     {
7494         get_tail_number(tempInt, value, "backblockpain");
7495         if(tempInt == 1)
7496         {
7497             ani_id = ANI_BACKBLOCKPAIN;
7498         }
7499         else if(tempInt == 2)
7500         {
7501             ani_id = ANI_BACKBLOCKPAIN2;
7502         }
7503         else if(tempInt == 3)
7504         {
7505             ani_id = ANI_BACKBLOCKPAIN3;
7506         }
7507         else if(tempInt == 4)
7508         {
7509             ani_id = ANI_BACKBLOCKPAIN4;
7510         }
7511         else if(tempInt == 5)
7512         {
7513             ani_id = ANI_BACKBLOCKPAIN5;
7514         }
7515         else if(tempInt == 6)
7516         {
7517             ani_id = ANI_BACKBLOCKPAIN6;
7518         }
7519         else if(tempInt == 7)
7520         {
7521             ani_id = ANI_BACKBLOCKPAIN7;
7522         }
7523         else if(tempInt == 8)
7524         {
7525             ani_id = ANI_BACKBLOCKPAIN8;
7526         }
7527         else if(tempInt == 9)
7528         {
7529             ani_id = ANI_BACKBLOCKPAIN9;
7530         }
7531         else if(tempInt == 10)
7532         {
7533             ani_id = ANI_BACKBLOCKPAIN10;
7534         }
7535         else
7536         {
7537             if(tempInt < MAX_ATKS - STA_ATKS + 1)
7538             {
7539                 tempInt = MAX_ATKS - STA_ATKS + 1;
7540             }
7541             ani_id = animbackblkpains[tempInt + STA_ATKS - 1];
7542         }
7543     }
7544     else if(stricmp(value, "duckattack") == 0)
7545     {
7546         ani_id = ANI_DUCKATTACK;
7547     }
7548     else if(stricmp(value, "walkoff") == 0)
7549     {
7550         ani_id = ANI_WALKOFF;
7551     }
7552     else if(stricmp(value, "edge") == 0)
7553     {
7554         ani_id = ANI_EDGE;
7555     }
7556     else if(stricmp(value, "backedge") == 0)
7557     {
7558         ani_id = ANI_BACKEDGE;
7559     }
7560     else if(stricmp(value, "ducking") == 0)
7561     {
7562         ani_id = ANI_DUCKING;
7563     }
7564     else if(stricmp(value, "duckrise") == 0)
7565     {
7566         ani_id = ANI_DUCKRISE;
7567     }
7568     else if(stricmp(value, "victory") == 0)
7569     {
7570         ani_id = ANI_VICTORY;
7571     }
7572     else if(stricmp(value, "lose") == 0)
7573     {
7574         ani_id = ANI_LOSE;
7575     }
7576 
7577     return ani_id;
7578 
7579 }
7580 
lcmHandleCommandName(ArgList * arglist,s_model * newchar,int cacheindex)7581 void lcmHandleCommandName(ArgList *arglist, s_model *newchar, int cacheindex)
7582 {
7583     char *value = GET_ARGP(1);
7584     s_model *tempmodel;
7585     if((tempmodel = findmodel(value)) && tempmodel != newchar)
7586     {
7587         borShutdown(1, "Duplicate model name '%s'", value);
7588     }
7589     /*if((tempmodel=find_model(value))) {
7590     	return tempmodel;
7591     }*/
7592     model_cache[cacheindex].model = newchar;
7593     newchar->name = model_cache[cacheindex].name;
7594     if(stricmp(newchar->name, "steam") == 0)
7595     {
7596         newchar->alpha = BLEND_MODE_ALPHA;
7597     }
7598 }
7599 
lcmHandleCommandType(ArgList * arglist,s_model * newchar,char * filename)7600 void lcmHandleCommandType(ArgList *arglist, s_model *newchar, char *filename)
7601 {
7602     char *value = GET_ARGP(1);
7603     int i;
7604     if(stricmp(value, "none") == 0)
7605     {
7606         newchar->type = TYPE_NONE;
7607     }
7608     else if(stricmp(value, "player") == 0)
7609     {
7610         newchar->type = TYPE_PLAYER;
7611         newchar->nopassiveblock = 1;
7612         for(i = 0; i < MAX_ATCHAIN; i++)
7613         {
7614             if(i < 2 || i > 3)
7615             {
7616                 newchar->atchain[i] = 1;
7617             }
7618             else
7619             {
7620                 newchar->atchain[i] = i;
7621             }
7622         }
7623         newchar->chainlength            = 4;
7624         newchar->bounce                 = 1;
7625         newchar->subject_to_basemap     = 1;
7626         newchar->subject_to_wall        = 1;
7627         newchar->subject_to_platform    = 1;
7628         newchar->subject_to_obstacle    = 1;
7629         newchar->subject_to_hole        = 1;
7630         newchar->subject_to_gravity     = 1;
7631         newchar->subject_to_screen      = 1;
7632         newchar->subject_to_minz        = 1;
7633         newchar->subject_to_maxz        = 1;
7634         newchar->no_adjust_base         = 0;
7635     }
7636     else if(stricmp(value, "enemy") == 0)
7637     {
7638         newchar->type                   = TYPE_ENEMY;
7639         newchar->bounce                 = 1;
7640         newchar->subject_to_basemap     = 1;
7641         newchar->subject_to_wall        = 1;
7642         newchar->subject_to_platform    = 1;
7643         newchar->subject_to_hole        = 1;
7644         newchar->subject_to_obstacle    = 1;
7645         newchar->subject_to_gravity     = 1;
7646         newchar->subject_to_minz        = 1;
7647         newchar->subject_to_maxz        = 1;
7648         newchar->no_adjust_base         = 0;
7649     }
7650     else if(stricmp(value, "item") == 0)
7651     {
7652         newchar->type                   = TYPE_ITEM;
7653         newchar->subject_to_basemap     = 1;
7654         newchar->subject_to_wall        = 1;
7655         newchar->subject_to_platform    = 1;
7656         newchar->subject_to_hole        = 1;
7657         newchar->subject_to_obstacle    = 1;
7658         newchar->subject_to_gravity     = 1;
7659         newchar->subject_to_minz        = 1;
7660         newchar->subject_to_maxz        = 1;
7661         newchar->no_adjust_base         = 0;
7662     }
7663     else if(stricmp(value, "obstacle") == 0)
7664     {
7665         newchar->type                   = TYPE_OBSTACLE;
7666         if(newchar->aimove == -1)
7667         {
7668             newchar->aimove = 0;
7669         }
7670         newchar->aimove |= AIMOVE1_NOMOVE;
7671         if(newchar->aimove == -1)
7672         {
7673             newchar->aiattack = 0;
7674         }
7675         newchar->aimove |= AIATTACK1_NOATTACK;
7676         newchar->subject_to_basemap     = 1;
7677         newchar->subject_to_wall        = 1;
7678         newchar->subject_to_platform    = 1;
7679         newchar->subject_to_hole        = 1;
7680         newchar->subject_to_gravity     = 1;
7681         newchar->subject_to_minz        = 1;
7682         newchar->subject_to_maxz        = 1;
7683         newchar->no_adjust_base         = 0;
7684     }
7685     else if(stricmp(value, "steamer") == 0)
7686     {
7687         newchar->offscreenkill = 80;
7688         newchar->type = TYPE_STEAMER;
7689     }
7690     // my new types   7-1-2005
7691     else if(stricmp(value, "pshot") == 0)
7692     {
7693         newchar->type = TYPE_SHOT;
7694         if(newchar->aimove == -1)
7695         {
7696             newchar->aimove = 0;
7697         }
7698         newchar->aimove |= AIMOVE1_ARROW;
7699         if(!newchar->offscreenkill)
7700         {
7701             newchar->offscreenkill = 200;
7702         }
7703         newchar->subject_to_hole                = 0;
7704         newchar->subject_to_gravity             = 1;
7705         newchar->subject_to_basemap             = 0;
7706         newchar->subject_to_wall                = 0;
7707         newchar->subject_to_platform            = 0;
7708         newchar->subject_to_screen              = 0;
7709         newchar->subject_to_minz                = 1;
7710         newchar->subject_to_maxz                = 1;
7711         newchar->subject_to_platform            = 0;
7712         newchar->no_adjust_base                 = 1;
7713     }
7714     else if(stricmp(value, "trap") == 0)
7715     {
7716         newchar->type                   = TYPE_TRAP;
7717         newchar->subject_to_basemap     = 1;
7718         newchar->subject_to_wall        = 1;
7719         newchar->subject_to_platform    = 1;
7720         newchar->subject_to_hole        = 1;
7721         newchar->subject_to_gravity     = 1;
7722         newchar->subject_to_minz        = 1;
7723         newchar->subject_to_maxz        = 1;
7724         newchar->no_adjust_base         = 0;
7725     }
7726     else if(stricmp(value, "text") == 0)   // Used for displaying text/images and freezing the screen
7727     {
7728         newchar->type                   = TYPE_TEXTBOX;
7729         newchar->subject_to_gravity     = 0;
7730         newchar->subject_to_minz        = 1;
7731         newchar->subject_to_maxz        = 1;
7732     }
7733     else if(stricmp(value, "endlevel") == 0)   // Used for ending the level when the players reach a certain point
7734     {
7735         newchar->type                   = TYPE_ENDLEVEL;
7736         newchar->subject_to_basemap     = 1;
7737         newchar->subject_to_wall        = 1;
7738         newchar->subject_to_platform    = 1;
7739         newchar->subject_to_hole        = 1;
7740         newchar->subject_to_obstacle    = 1;
7741         newchar->subject_to_gravity     = 1;
7742     }
7743     else if(stricmp(value, "npc") == 0)   // NPC type
7744     {
7745         newchar->type                   = TYPE_NPC;
7746         newchar->bounce                 = 1;
7747         newchar->subject_to_basemap     = 1;
7748         newchar->subject_to_wall        = 1;
7749         newchar->subject_to_platform    = 1;
7750         newchar->subject_to_hole        = 1;
7751         newchar->subject_to_obstacle    = 1;
7752         newchar->subject_to_gravity     = 1;
7753         newchar->subject_to_minz        = 1;
7754         newchar->subject_to_maxz        = 1;
7755         newchar->no_adjust_base         = 0;
7756     }
7757     else if(stricmp(value, "panel") == 0)   // NPC type
7758     {
7759         newchar->type                   = TYPE_PANEL;
7760         newchar->antigravity            = 1.0; //float type
7761         newchar->subject_to_gravity     = 1;
7762         newchar->no_adjust_base         = 1;
7763     }
7764     else
7765     {
7766         borShutdown(1, "Model '%s' has invalid type: '%s'", filename, value);
7767     }
7768 }
7769 
lcmHandleCommandSubtype(ArgList * arglist,s_model * newchar,char * filename)7770 void lcmHandleCommandSubtype(ArgList *arglist, s_model *newchar, char *filename)
7771 {
7772     char *value = GET_ARGP(1);
7773     int i;
7774     if(stricmp(value, "biker") == 0)
7775     {
7776         newchar->subtype                                        = SUBTYPE_BIKER;
7777         if(newchar->aimove == -1)
7778         {
7779             newchar->aimove                 = 0;
7780         }
7781         newchar->aimove |= AIMOVE1_BIKER;
7782         if(!newchar->offscreenkill)
7783         {
7784             newchar->offscreenkill = 300;
7785         }
7786         for(i = 0; i < max_attack_types; i++)
7787         {
7788             newchar->defense[i].factor = 2.f;
7789         }
7790         newchar->subject_to_hole                                = 1;
7791         newchar->subject_to_gravity                             = 1;
7792         newchar->subject_to_basemap                             = 0;
7793         newchar->subject_to_wall                                = 0;
7794         newchar->subject_to_platform                            = 0;
7795         newchar->subject_to_screen                              = 0;
7796         newchar->subject_to_minz                                = 1;
7797         newchar->subject_to_maxz                                = 1;
7798         newchar->no_adjust_base                                 = 0;
7799     }
7800     else if(stricmp(value, "arrow") == 0) // 7-1-2005 Arrow type
7801     {
7802         newchar->subtype = SUBTYPE_ARROW;   // 7-1-2005 Arrow type
7803         if(newchar->aimove == -1)
7804         {
7805             newchar->aimove = 0;
7806         }
7807         newchar->aimove |= AIMOVE1_ARROW;
7808         if(!newchar->offscreenkill)
7809         {
7810             newchar->offscreenkill = 200;
7811         }
7812         newchar->subject_to_hole        = 0;
7813         newchar->subject_to_gravity     = 1;
7814         newchar->subject_to_basemap     = 0;
7815         newchar->subject_to_wall        = 0;
7816         newchar->subject_to_platform    = 0;
7817         newchar->subject_to_screen      = 0;
7818         newchar->subject_to_minz        = 1;
7819         newchar->subject_to_maxz        = 1;
7820         newchar->no_adjust_base         = 1;
7821     }
7822     else if(stricmp(value, "notgrab") == 0)
7823     {
7824         newchar->subtype = SUBTYPE_NOTGRAB;
7825     }
7826     //    ltb 1-18-05  Item Subtype
7827     else if(stricmp(value, "touch") == 0)
7828     {
7829         newchar->subtype = SUBTYPE_TOUCH;
7830     }
7831     else if(stricmp(value, "weapon") == 0)
7832     {
7833         newchar->subtype = SUBTYPE_WEAPON;
7834     }
7835     else if(stricmp(value, "noskip") == 0)   // Text animation cannot be skipped if subtype noskip
7836     {
7837         newchar->subtype = SUBTYPE_NOSKIP;
7838     }
7839     else if(stricmp(value, "flydie") == 0)   // Obstacle will fly across the screen when hit if subtype flydie
7840     {
7841         newchar->subtype = SUBTYPE_FLYDIE;
7842     }
7843     else if(stricmp(value, "both") == 0)
7844     {
7845         newchar->subtype = SUBTYPE_BOTH;
7846     }
7847     else if(stricmp(value, "project") == 0)
7848     {
7849         newchar->subtype = SUBTYPE_PROJECTILE;
7850     }
7851     else if(stricmp(value, "follow") == 0)
7852     {
7853         newchar->subtype = SUBTYPE_FOLLOW;
7854     }
7855     else if(stricmp(value, "chase") == 0)
7856     {
7857         newchar->subtype = SUBTYPE_CHASE;
7858     }
7859     //    end new subtype
7860     else
7861     {
7862         borShutdown(1, "Model '%s' has invalid subtype: '%s'", filename, value);
7863     }
7864 }
7865 
lcmHandleCommandSmartbomb(ArgList * arglist,s_model * newchar,char * filename)7866 void lcmHandleCommandSmartbomb(ArgList *arglist, s_model *newchar, char *filename)
7867 {
7868     //smartbomb now use a normal attack box
7869     if(!newchar->smartbomb)
7870     {
7871         newchar->smartbomb = malloc(sizeof(*newchar->smartbomb));
7872         *(newchar->smartbomb) = emptyattack;
7873     }
7874     else
7875     {
7876         borShutdown(1, "Model '%s' has multiple smartbomb commands defined.", filename);
7877     }
7878 
7879     newchar->smartbomb->attack_force = atoi(GET_ARGP(1));			// Special force
7880     newchar->smartbomb->attack_type = atoi(GET_ARGP(2));			// Special attack type
7881     newchar->smartbomb->attack_drop = 1; //by default
7882     newchar->smartbomb->dropv.y = default_model_dropv.y;
7883 
7884     if(newchar->smartbomb->attack_type == ATK_BLAST)
7885     {
7886         newchar->smartbomb->blast = 1;
7887         newchar->smartbomb->dropv.x = default_model_dropv.x * 2.083f;
7888     }
7889     else
7890     {
7891         newchar->smartbomb->dropv.x = default_model_dropv.x;
7892     }
7893 
7894     if(newchar->smartbomb->attack_type == ATK_FREEZE)
7895     {
7896         newchar->smartbomb->freeze = 1;
7897         newchar->smartbomb->forcemap = -1;
7898         newchar->smartbomb->attack_drop = 0;
7899     }
7900     else if(newchar->smartbomb->attack_type == ATK_STEAL)
7901     {
7902         newchar->smartbomb->steal = 1;
7903     }
7904 
7905     if(newchar->type == TYPE_ITEM)
7906     {
7907         newchar->dofreeze = 0;								// Items don't animate
7908         newchar->smartbomb->freezetime = atoi(GET_ARGP(3)) * GAME_SPEED;
7909     }
7910     else
7911     {
7912         newchar->dofreeze = atoi(GET_ARGP(3));		// Are all animations frozen during special
7913         newchar->smartbomb->freezetime = atoi(GET_ARGP(4)) * GAME_SPEED;
7914     }
7915 }
7916 
lcmHandleCommandHostile(ArgList * arglist,s_model * newchar)7917 void lcmHandleCommandHostile(ArgList *arglist, s_model *newchar)
7918 {
7919     int i;
7920     char *value;
7921     newchar->hostile = 0;
7922 
7923     for(i = 1; (value = GET_ARGP(i)) && value[0]; i++)
7924     {
7925         if(stricmp(value, "enemy") == 0)
7926         {
7927             newchar->hostile |= TYPE_ENEMY;
7928         }
7929         else if(stricmp(value, "player") == 0)
7930         {
7931             newchar->hostile |= TYPE_PLAYER;
7932         }
7933         else if(stricmp(value, "obstacle") == 0)
7934         {
7935             newchar->hostile |= TYPE_OBSTACLE;
7936         }
7937         else if(stricmp(value, "shot") == 0)
7938         {
7939             newchar->hostile |= TYPE_SHOT;
7940         }
7941         else if(stricmp(value, "npc") == 0)
7942         {
7943             newchar->hostile |= TYPE_NPC;
7944         }
7945         else
7946         {
7947             newchar->hostile |= atoi(value); //debug raw integer value
7948         }
7949     }
7950 }
lcmHandleCommandCandamage(ArgList * arglist,s_model * newchar)7951 void lcmHandleCommandCandamage(ArgList *arglist, s_model *newchar)
7952 {
7953     int i;
7954     char *value;
7955     newchar->candamage = 0;
7956 
7957     for(i = 1; (value = GET_ARGP(i)) && value[0]; i++)
7958     {
7959         if(stricmp(value, "enemy") == 0)
7960         {
7961             newchar->candamage |= TYPE_ENEMY;
7962         }
7963         else if(stricmp(value, "player") == 0)
7964         {
7965             newchar->candamage |= TYPE_PLAYER;
7966         }
7967         else if(stricmp(value, "obstacle") == 0)
7968         {
7969             newchar->candamage |= TYPE_OBSTACLE;
7970         }
7971         else if(stricmp(value, "shot") == 0)
7972         {
7973             newchar->candamage |= TYPE_SHOT;
7974         }
7975         else if(stricmp(value, "npc") == 0)
7976         {
7977             newchar->candamage |= TYPE_NPC;
7978         }
7979         else if(stricmp(value, "ground") == 0)  // not really needed, though
7980         {
7981             newchar->ground = 1;
7982         }
7983         else
7984         {
7985             newchar->candamage |= atoi(value); //debug raw integer value
7986         }
7987     }
7988 }
7989 
lcmHandleCommandProjectilehit(ArgList * arglist,s_model * newchar)7990 void lcmHandleCommandProjectilehit(ArgList *arglist, s_model *newchar)
7991 {
7992     int i;
7993     char *value;
7994     newchar->projectilehit = 0;
7995 
7996     for(i = 1; (value = GET_ARGP(i)) && value[0]; i++)
7997     {
7998         if(stricmp(value, "enemy") == 0)
7999         {
8000             newchar->projectilehit |= TYPE_ENEMY;
8001         }
8002         else if(stricmp(value, "player") == 0)
8003         {
8004             newchar->projectilehit |= TYPE_PLAYER;
8005         }
8006         else if(stricmp(value, "obstacle") == 0)
8007         {
8008             newchar->projectilehit |= TYPE_OBSTACLE;
8009         }
8010         else if(stricmp(value, "shot") == 0)
8011         {
8012             newchar->projectilehit |= TYPE_SHOT;
8013         }
8014         else if(stricmp(value, "npc") == 0)
8015         {
8016             newchar->projectilehit |= TYPE_NPC;
8017         }
8018         else
8019         {
8020             newchar->projectilehit |= atoi(value); //debug raw integer value
8021         }
8022     }
8023 }
8024 
lcmHandleCommandAimove(ArgList * arglist,s_model * newchar,int * aimoveset,char * filename)8025 void lcmHandleCommandAimove(ArgList *arglist, s_model *newchar, int *aimoveset, char *filename)
8026 {
8027     char *value = GET_ARGP(1);
8028     if(!*aimoveset)
8029     {
8030         newchar->aimove = 0;
8031         *aimoveset = 1;
8032     }
8033 
8034     //main A.I. move switches
8035     if(value && value[0])
8036     {
8037         if(stricmp(value, "normal") == 0)
8038         {
8039             newchar->aimove |= AIMOVE1_NORMAL;
8040         }
8041         else if(stricmp(value, "chase") == 0)
8042         {
8043             newchar->aimove |= AIMOVE1_CHASE;
8044         }
8045         else if(stricmp(value, "chasex") == 0)
8046         {
8047             newchar->aimove |= AIMOVE1_CHASEX;
8048         }
8049         else if(stricmp(value, "chasez") == 0)
8050         {
8051             newchar->aimove |= AIMOVE1_CHASEZ;
8052         }
8053         else if(stricmp(value, "avoid") == 0)
8054         {
8055             newchar->aimove |= AIMOVE1_AVOID;
8056         }
8057         else if(stricmp(value, "avoidx") == 0)
8058         {
8059             newchar->aimove |= AIMOVE1_AVOIDX;
8060         }
8061         else if(stricmp(value, "avoidz") == 0)
8062         {
8063             newchar->aimove |= AIMOVE1_AVOIDZ;
8064         }
8065         else if(stricmp(value, "wander") == 0)
8066         {
8067             newchar->aimove |= AIMOVE1_WANDER;
8068         }
8069         else if(stricmp(value, "biker") == 0)
8070         {
8071             newchar->aimove |= AIMOVE1_BIKER;
8072         }
8073         else if(stricmp(value, "arrow") == 0)
8074         {
8075             newchar->aimove |= AIMOVE1_ARROW;
8076             if(!newchar->offscreenkill)
8077             {
8078                 newchar->offscreenkill = 200;
8079             }
8080         }
8081         else if(stricmp(value, "star") == 0)
8082         {
8083             newchar->aimove |= AIMOVE1_STAR;
8084         }
8085         else if(stricmp(value, "bomb") == 0)
8086         {
8087             newchar->aimove |= AIMOVE1_BOMB;
8088         }
8089         else if(stricmp(value, "nomove") == 0)
8090         {
8091             newchar->aimove |= AIMOVE1_NOMOVE;
8092         }
8093         else
8094         {
8095             borShutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename, value);
8096         }
8097     }
8098     value = GET_ARGP(2);
8099     //sub A.I. move switches
8100     if(value && value[0])
8101     {
8102         if(stricmp(value, "normal") == 0)
8103         {
8104             newchar->aimove |= AIMOVE2_NORMAL;
8105         }
8106         else if(stricmp(value, "ignoreholes") == 0)
8107         {
8108             newchar->aimove |= AIMOVE2_IGNOREHOLES;
8109         }
8110         else if(stricmp(value, "notargetidle") == 0)
8111         {
8112             newchar->aimove |= AIMOVE2_NOTARGETIDLE;
8113         }
8114         else
8115         {
8116             borShutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename, value);
8117         }
8118     }
8119 }
lcmHandleCommandAiattack(ArgList * arglist,s_model * newchar,int * aiattackset,char * filename)8120 void lcmHandleCommandAiattack(ArgList *arglist, s_model *newchar, int *aiattackset, char *filename)
8121 {
8122     char *value = GET_ARGP(1);
8123     if(!*aiattackset)
8124     {
8125         newchar->aiattack = 0;
8126         *aiattackset = 1;
8127     }
8128 
8129     //main A.I. move switches
8130     if(value && value[0])
8131     {
8132         if(stricmp(value, "normal") == 0)
8133         {
8134             newchar->aiattack |= AIATTACK1_NORMAL;
8135         }
8136         else if(stricmp(value, "always") == 0)
8137         {
8138             newchar->aiattack |= AIATTACK1_ALWAYS;
8139         }
8140         else if(stricmp(value, "noattack") == 0)
8141         {
8142             newchar->aiattack |= AIATTACK1_NOATTACK;
8143         }
8144         else
8145         {
8146             printf("Model '%s' has invalid A.I. attack switch: '%s'\n", filename, value);
8147         }
8148     }
8149     /*
8150     value = GET_ARGP(2);
8151     //sub A.I. move switches
8152     if(value && value[0])
8153     {
8154 
8155     }*/
8156 }
8157 
lcmHandleCommandWeapons(ArgList * arglist,s_model * newchar)8158 void lcmHandleCommandWeapons(ArgList *arglist, s_model *newchar)
8159 {
8160     int weap;
8161     char *value;
8162     for(weap = 0; ; weap++)
8163     {
8164         value = GET_ARGP(weap + 1);
8165         if(!value[0])
8166         {
8167             break;
8168         }
8169     }
8170 
8171     if(!weap)
8172     {
8173         return;
8174     }
8175 
8176     newchar->numweapons = weap;
8177 
8178     if(!newchar->weapon)
8179     {
8180         newchar->weapon = malloc(sizeof(*newchar->weapon) * newchar->numweapons);
8181         memset(newchar->weapon, 0xFF, sizeof(*newchar->weapon)*newchar->numweapons);
8182         newchar->ownweapons = 1;
8183     }
8184     for(weap = 0; weap < newchar->numweapons ; weap++)
8185     {
8186         value = GET_ARGP(weap + 1);
8187         if(stricmp(value, "none") != 0)
8188         {
8189             newchar->weapon[weap] = get_cached_model_index(value);
8190         }
8191         else     // make empty weapon slots  2007-2-16
8192         {
8193             newchar->weapon[weap] = -1;
8194         }
8195     }
8196 }
8197 
8198 //fetch string between next @script and @end_script
8199 //return end position of @end_script, count from current position
fetchInlineScript(char * buf,char ** scriptbuf,ptrdiff_t * ppos,size_t * plen)8200 static size_t fetchInlineScript(char *buf, char **scriptbuf, ptrdiff_t *ppos, size_t *plen)
8201 {
8202     ptrdiff_t pos = *ppos;
8203     size_t len;
8204     while(!starts_with(buf + pos, "@script"))
8205     {
8206         pos++;
8207     }
8208     pos += strclen("@script");
8209     len = 0;
8210     while(!starts_with(buf + pos, "@end_script"))
8211     {
8212         len++;
8213         pos++;
8214     }
8215     *scriptbuf = malloc(sizeof(**scriptbuf) * len + 1);
8216     strncpy(*scriptbuf, buf + pos - len, len);
8217     (*scriptbuf)[len] = 0;
8218     pos += strclen("@end_script");
8219 
8220     *ppos = pos;
8221     *plen = len;
8222     return pos;
8223 }
8224 
lcmHandleCommandScripts(ArgList * arglist,char * buf,Script * script,char * scriptname,char * filename,int compile,int first)8225 size_t lcmHandleCommandScripts(ArgList *arglist, char *buf, Script *script, char *scriptname, char *filename, int compile, int first)
8226 {
8227     ptrdiff_t pos = 0;
8228     size_t len = 0;
8229     int result = 0;
8230     char *scriptbuf = NULL;
8231     Script_Init(script, scriptname, filename, first);
8232     if(stricmp(GET_ARGP(1), "@script") == 0)
8233     {
8234         fetchInlineScript(buf, &scriptbuf, &pos, &len);
8235         if(scriptbuf)
8236         {
8237             result = Script_AppendText(script, scriptbuf, filename);
8238             free(scriptbuf);
8239         }
8240     }
8241     else
8242     {
8243         result = load_script(script, GET_ARGP(1));
8244     }
8245     if(result)
8246     {
8247         if(compile)
8248         {
8249             Script_Compile(script);
8250         }
8251     }
8252     else
8253     {
8254         borShutdown(1, "Unable to load %s '%s' in file '%s'.\n", scriptname, GET_ARGP(1), filename);
8255     }
8256     return pos;
8257 }
8258 
lcmScriptAvoidComment(char * buf,size_t buf_len,ptrdiff_t pos)8259 ptrdiff_t lcmScriptAvoidComment(char *buf, size_t buf_len, ptrdiff_t pos)
8260 {
8261     if ( buf && buf[0] )
8262     {
8263         unsigned char c = '\0';
8264         ptrdiff_t pre_pos = pos;
8265 
8266         c = buf[pos];
8267         if (c == '/')
8268         {
8269             ++pos;
8270             if ( pos < buf_len-1 )
8271             {
8272                 c = buf[pos];
8273                 if (c == '/')
8274                 {
8275                     while( pos < buf_len && c != 0x0D && c != 0x0A )
8276                     {
8277                         ++pos;
8278                         c = buf[pos];
8279                     }
8280                     if (pos < buf_len) ++pos;
8281                     pos = lcmScriptAvoidComment(buf,buf_len,pos);
8282                     return pos;
8283                 }
8284                 else if (c == '*')
8285                 {
8286                     ++pos;
8287                     if ( pos >= buf_len ) return pos;
8288                     c = buf[pos];
8289                     while( pos+1 < buf_len )
8290                     {
8291                         if ( c == '*' )
8292                         {
8293                             unsigned char c2 = buf[pos+1];
8294 
8295                             if ( c2 == '/' )
8296                             {
8297                                 pos += 2;
8298                                 pos = lcmScriptAvoidComment(buf,buf_len,pos);
8299                                 return pos;
8300                             }
8301                         }
8302                         ++pos;
8303                         c = buf[pos];
8304                     }
8305                 }
8306                 else
8307                 {
8308                     return pre_pos;
8309                 }
8310             }
8311         } else return pre_pos;
8312     }
8313 
8314     return pos;
8315 }
8316 
lcmScriptGetMainPos(char * buf,size_t * buf_len)8317 ptrdiff_t lcmScriptGetMainPos(char *buf, size_t *buf_len)
8318 {
8319     if ( buf && buf[0] )
8320     {
8321         size_t len = *buf_len;
8322         ptrdiff_t pos = 0;
8323         enum {START,PRE0,PRE1,PRE2,PRE3,M0,M1,M2,M3,P0,P1,END} current_state = START;
8324         unsigned char c = '\0';
8325         int index_res = -1;
8326 
8327         pos = lcmScriptAvoidComment(buf,len,pos);
8328         c = (unsigned char)tolower((int)buf[pos]);
8329         while ( pos < len && current_state != END )
8330         {
8331             switch(current_state)
8332             {
8333                 case START:
8334                 {
8335                     if ( c == 'v' )
8336                     {
8337                         index_res = pos; // store void main() start pos
8338                         current_state = PRE0;
8339                     }
8340                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = START;
8341                     else if ( c == '\0' ) return -1;
8342                     else current_state = START;
8343                     break;
8344                 }
8345                 case PRE0:
8346                 {
8347                     if ( c == 'o' ) current_state = PRE1;
8348                     else if ( c == '\0' ) return -1;
8349                     else current_state = START;
8350                     break;
8351                 }
8352                 case PRE1:
8353                 {
8354                     if ( c == 'i' ) current_state = PRE2;
8355                     else if ( c == '\0' ) return -1;
8356                     else current_state = START;
8357                     break;
8358                 }
8359                 case PRE2:
8360                 {
8361                     if ( c == 'd' ) current_state = PRE3;
8362                     else if ( c == '\0' ) return -1;
8363                     else current_state = START;
8364                     break;
8365                 }
8366                 case PRE3:
8367                 {
8368                     if ( c == 'm' ) current_state = M0;
8369                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = PRE3;
8370                     else if ( c == '\0' ) return -1;
8371                     else current_state = START;
8372                     break;
8373                 }
8374                 case M0:
8375                 {
8376                     if ( c == 'a' ) current_state = M1;
8377                     else if ( c == '\0' ) return -1;
8378                     else current_state = START;
8379                     break;
8380                 }
8381                 case M1:
8382                 {
8383                     if ( c == 'i' ) current_state = M2;
8384                     else if ( c == '\0' ) return -1;
8385                     else current_state = START;
8386                     break;
8387                 }
8388                 case M2:
8389                 {
8390                     if ( c == 'n' ) current_state = M3;
8391                     else if ( c == '\0' ) return -1;
8392                     else current_state = START;
8393                     break;
8394                 }
8395                 case M3:
8396                 {
8397                     if ( c == '(' ) current_state = P0;
8398                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = M3;
8399                     else if ( c == '\0' ) return -1;
8400                     else current_state = START;
8401                     break;
8402                 }
8403                 case P0:
8404                 {
8405                     if ( c == ')' ) current_state = P1;
8406                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = P0;
8407                     else if ( c == '\0' ) return -1;
8408                     else current_state = START;
8409                     break;
8410                 }
8411                 case P1:
8412                 {
8413                     if ( c == '{' )
8414                     {
8415                         current_state = END;
8416                         if (++pos < len)
8417                         {
8418                             *buf_len = pos-index_res;
8419                             return index_res;
8420                         }
8421                         else return -1;
8422                     }
8423                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = P1;
8424                     else if ( c == '\0' ) return -1;
8425                     else current_state = START;
8426                     break;
8427                 }
8428                 case END:
8429                 default:
8430                     break;
8431             }
8432             ++pos; // at the end position after the '{'
8433             if (current_state == END)
8434             {
8435                 if (pos < len)
8436                 {
8437                     *buf_len = pos-index_res;
8438                     return index_res;
8439                 }
8440                 else return -1;
8441             }
8442             pos = lcmScriptAvoidComment(buf,len,pos);
8443             c = (unsigned char)tolower((int)buf[pos]);
8444         } // end while loop
8445 
8446         if (current_state == END)
8447         {
8448             if (pos < len)
8449             {
8450                 *buf_len = pos-index_res;
8451                 return index_res;
8452             }
8453             else return -1;
8454         } else return -1;
8455     }
8456 
8457     return -1;
8458 }
8459 
8460 // add main (or add vars in main) - used for animationscript file
lcmScriptAddMain(char ** buf)8461 size_t lcmScriptAddMain(char **buf)
8462 {
8463     size_t len = 0, len2 = 0, buf_len = 0;
8464     ptrdiff_t pos = 0;
8465     char* newbuf = NULL;
8466 
8467     if ( (*buf) && (*buf)[0] )
8468     {
8469         buf_len = len = strlen(*buf);
8470 
8471         pos = lcmScriptGetMainPos(*buf,&buf_len);
8472         if ( pos == -1 ) // main() not found!
8473         {
8474             char mtxt[] = "\n\nvoid main()\n{\n    int frame = getlocalvar(\"frame\");\n    int animhandle = getlocalvar(\"animhandle\");\n\n}\n\n";
8475 
8476             pos += buf_len;
8477             pos = len; // pos before '\0' (at last char)
8478             len2 = strlen(mtxt);
8479             newbuf = calloc(sizeof(**buf)*len + sizeof(mtxt)*len2 + 1, sizeof(char));
8480             strncpy(newbuf, *buf, pos);
8481             strcpy(newbuf+pos, mtxt);
8482             newbuf[len+len2] = '\0';
8483 
8484             free( (*buf) );
8485             (*buf) = newbuf;
8486 
8487             //printf("written main in buffer\n%s\n",newbuf);
8488         }
8489         else
8490         {
8491             int pos2 = 0;
8492             char mtxt[] = "\n\nvoid main()\n{\n    int frame = getlocalvar(\"frame\");\n    int animhandle = getlocalvar(\"animhandle\");\n\n";
8493 
8494             pos2 = pos + buf_len;
8495             len2 = strlen(mtxt);
8496 
8497             newbuf = calloc(sizeof(**buf)*pos + sizeof(mtxt)*len2 + sizeof(**buf)*(len-pos2) + 1, sizeof(char));
8498             strncpy(newbuf, *buf, pos);
8499             strcpy(newbuf+pos, mtxt);
8500             strncpy(newbuf+pos+len2, *buf+pos2, len-pos2);
8501             newbuf[pos+len2+len-pos2] = '\0';
8502 
8503             free( (*buf) );
8504             (*buf) = newbuf;
8505 
8506             //printf("search for main in buffer\n%s pos:%d\n",*buf,pos);
8507         }
8508 
8509         //printf("search for main in buffer\n%s pos:%d\n",*buf,pos);
8510     }
8511 
8512     return len;
8513 }
8514 
8515 // used to join animationscript @script/@cmd width animationscript file main()
lcmScriptJoinMain(char ** buf,char * first_buf)8516 size_t lcmScriptJoinMain(char **buf, char *first_buf)
8517 {
8518     size_t len = 0, len2 = 0, buf_len = 0;
8519     ptrdiff_t pos = 0;
8520     int pcount = 0;
8521     char* newbuf = NULL;
8522 
8523     if ( (*buf) && first_buf && (*buf)[0] && first_buf[0] )
8524     {
8525         buf_len = len = strlen(*buf);
8526         len2 = strlen(first_buf);
8527 
8528         pos = lcmScriptGetMainPos(*buf,&buf_len);
8529         pos += buf_len;
8530 
8531         pcount = 1;
8532         while(pcount > 0)
8533         {
8534             if( (*buf)[pos] == '{' ) ++pcount;
8535             else if( (*buf)[pos] == '}' ) --pcount;
8536             pos++;
8537         }
8538         --pos; // back to last '}' of main()
8539 
8540         newbuf = calloc(sizeof(**buf)*len + sizeof(*first_buf)*len2 + 1,sizeof(char));
8541         strncpy(newbuf, *buf, pos);
8542         strcpy(newbuf+pos, first_buf);
8543         strncpy(newbuf+pos+len2, *buf+pos, len-pos);
8544         newbuf[len+len2] = '\0';
8545 
8546         free( (*buf) );
8547         (*buf) = newbuf;
8548 
8549         //printf("test joined buffer\n%s\n",*buf);
8550     }
8551 
8552     return len;
8553 }
8554 
lcmScriptCopyBuffer(ArgList * arglist,char * buf,char ** script_buffer)8555 size_t lcmScriptCopyBuffer(ArgList *arglist, char *buf, char **script_buffer)
8556 {
8557     ptrdiff_t pos = 0;
8558     size_t len = 0;
8559 
8560     if(stricmp(GET_ARGP(1), "@script") == 0)
8561     {
8562         fetchInlineScript(buf, script_buffer, &pos, &len);
8563     }
8564     else
8565     {
8566         buffer_pakfile(GET_ARGP(1), script_buffer, &len);
8567     }
8568 
8569     //printf("test buffer\n%s\n",*script_buffer);
8570     return pos;
8571 }
8572 
lcmScriptDeleteMain(char ** buf)8573 size_t lcmScriptDeleteMain(char **buf)
8574 {
8575     size_t len = 0;
8576     long i = 0;
8577     ptrdiff_t pos = 0;
8578     char *newbuf = NULL;
8579 
8580     if ((*buf) && (*buf)[0])
8581     {
8582         len = strlen(*buf);
8583 
8584         while(!starts_with((*buf) + pos, "main()")) pos++;
8585         pos += strclen("main()");
8586         while(!starts_with(*buf + pos, "{")) pos++;
8587         pos += strclen("{");
8588         while(!starts_with(*buf + pos, "int frame = getlocalvar(\"frame\");")) pos++;
8589         pos += strclen("int frame = getlocalvar(\"frame\");");
8590         while(!starts_with(*buf + pos, "int animhandle = getlocalvar(\"animhandle\");\n")) pos++;
8591         pos += strclen("int animhandle = getlocalvar(\"animhandle\");\n");
8592 
8593         for(i = len-1; ; i--)
8594         {
8595             if ( (*buf)[i] == '}' )
8596             {
8597                 len = i;
8598                 break;
8599             } else continue;
8600             if (i <= 0) break;
8601         }
8602 
8603         len = len-pos;
8604         newbuf = malloc(sizeof(newbuf) * (len) + 1);
8605         strncpy(newbuf, *buf+pos, len);
8606         newbuf[len] = '\0';
8607 
8608         free( (*buf) );
8609         (*buf) = newbuf;
8610 
8611         //printf("test delete main\n%s\n",newbuf);
8612     }
8613 
8614     return len;
8615 }
8616 
8617 //alloc a new model, and everything thats required,
8618 //set all values to defaults
init_model(int cacheindex,int unload)8619 s_model *init_model(int cacheindex, int unload)
8620 {
8621     //to free: newchar, newchar->offense_factors, newchar->special, newchar->animation - OK
8622     int i;
8623 
8624     s_model *newchar = calloc(1, sizeof(*newchar));
8625     if(!newchar)
8626     {
8627         borShutdown(1, (char *)E_OUT_OF_MEMORY);
8628     }
8629     newchar->name = model_cache[cacheindex].name; // well give it a name for sort method
8630     newchar->index = cacheindex;
8631     newchar->isSubclassed = 0;
8632     newchar->freetypes = MF_ALL;
8633 
8634     newchar->priority = 1;
8635 
8636     newchar->defense		        = calloc(max_attack_types + 1, sizeof(*newchar->defense));
8637     newchar->offense_factors        = calloc(max_attack_types + 1, sizeof(*newchar->offense_factors));
8638 
8639     newchar->special                = calloc(1, sizeof(s_com));
8640 
8641     alloc_all_scripts(&newchar->scripts);
8642 
8643     newchar->unload             = unload;
8644     newchar->jumpspeed          = default_model_jumpspeed;
8645     newchar->jumpheight         = default_model_jumpheight; // 28-12-2004   Set default jump height to 4, if not specified
8646     newchar->runjumpheight      = default_model_jumpheight; // Default jump height if a player is running
8647     newchar->runjumpdist        = 1; // Default jump distane if a player is running
8648     newchar->grabdistance       = default_model_grabdistance; //  30-12-2004 Default grabdistance is same as originally set
8649     newchar->grabflip		    = 3;
8650     newchar->throwdamage        = 21; //21 // default throw damage
8651     newchar->icon.def			= -1;
8652     newchar->icon.die           = -1;
8653     newchar->icon.pain          = -1;
8654     newchar->icon.get           = -1;
8655     newchar->icon.weapon		= -1;			    // No weapon icon set yet
8656     newchar->diesound           = -1;
8657     newchar->nolife             = 0;			    // default show life = 1 (yes)
8658     newchar->remove             = 1;			    // Flag set to weapons are removed upon hitting an opponent
8659     newchar->throwdist          = default_model_jumpheight * 0.625f;
8660     newchar->counter            = 3;			    // Default 3 times to drop a weapon / projectile
8661     newchar->aimove             = -1;
8662     newchar->aiattack           = -1;
8663     newchar->throwframewait     = -1;               // makes sure throw animations run normally unless throwfram is specified, added by kbandressen 10/20/06
8664     newchar->path               = model_cache[cacheindex].path;         // Record path, so script can get it without looping the whole model collection.
8665     newchar->icon.mphigh        = -1;               //No mphigh icon yet.
8666     newchar->icon.mplow         = -1;               //No mplow icon yet.
8667     newchar->icon.mpmed         = -1;               //No mpmed icon yet.
8668     newchar->edgerange.x        = 0;
8669     newchar->edgerange.z        = 0;
8670 
8671     // Default Attack1 in chain must be referenced if not used.
8672     for(i = 0; i < MAX_ATCHAIN; i++)
8673     {
8674         newchar->atchain[i] = 1;
8675     }
8676     newchar->chainlength = 1;
8677 
8678     if(magic_type == 1)
8679     {
8680         newchar->mprate = 1;
8681     }
8682     else
8683     {
8684         newchar->mprate                 = 2;
8685     }
8686     newchar->chargerate = newchar->guardrate = 2;
8687     newchar->risetime.rise              = -1;
8688     newchar->sleepwait                  = 1000;
8689     newchar->jugglepoints.current = newchar->jugglepoints.max = 0;
8690     newchar->guardpoints.current = newchar->guardpoints.max = 0;
8691     newchar->mpswitch                   = -1;       // switch between reduce mp or gain mp for mpstabletype 4
8692     newchar->weaploss[0]                = WEAPLOSS_TYPE_ANY;
8693     newchar->weaploss[1]                = -1;
8694     newchar->lifespan                   = LIFESPAN_DEFAULT;
8695     newchar->summonkill                 = 1;
8696     newchar->candamage                  = -1;
8697     newchar->hostile                    = -1;
8698     newchar->projectilehit              = -1;
8699     newchar->subject_to_basemap         = -1;
8700     newchar->subject_to_wall            = -1;
8701     newchar->subject_to_platform        = -1;
8702     newchar->subject_to_obstacle        = -1;
8703     newchar->subject_to_hole            = -1;
8704     newchar->subject_to_gravity         = -1;
8705     newchar->subject_to_screen          = -1;
8706     newchar->subject_to_minz            = -1;
8707     newchar->subject_to_maxz            = -1;
8708     newchar->no_adjust_base             = -1;
8709     newchar->pshotno                    = -1;
8710     newchar->project                    = -1;
8711     newchar->dust.fall_land             = -1;
8712     newchar->dust.jump_land             = -1;
8713     newchar->dust.jump_start            = -1;
8714     newchar->bomb                       = -1;
8715     newchar->star                       = -1;
8716     newchar->knife                      = -1;
8717     newchar->stealth.hide               = 0;
8718     newchar->stealth.detect             = 0;
8719     newchar->attackthrottle				= 0.0f;
8720     newchar->attackthrottletime			= noatk_duration * GAME_SPEED;
8721 
8722     newchar->animation = calloc(max_animations, sizeof(*newchar->animation));
8723     if(!newchar->animation)
8724     {
8725         borShutdown(1, (char *)E_OUT_OF_MEMORY);
8726     }
8727 
8728     // default string value, only by reference
8729     newchar->rider = get_cached_model_index("K'");
8730     newchar->flash = newchar->bflash = get_cached_model_index("flash");
8731 
8732     //Default offense/defense values.
8733     for(i = 0; i < max_attack_types; i++)
8734     {
8735         newchar->offense_factors[i]     = 1;
8736         newchar->defense[i]				= default_defense;
8737     }
8738 
8739     //Default sight ranges.
8740     newchar->sight.min.x = -9999;
8741     newchar->sight.max.x = 9999;
8742     newchar->sight.min.y = -9999;
8743     newchar->sight.max.y = 9999;
8744     newchar->sight.min.z = -9999;
8745     newchar->sight.max.z = 9999;
8746 
8747     return newchar;
8748 }
8749 
update_model_loadflag(s_model * model,char unload)8750 void update_model_loadflag(s_model *model, char unload)
8751 {
8752     model->unload = unload;
8753 }
8754 
load_cached_model(char * name,char * owner,char unload)8755 s_model *load_cached_model(char *name, char *owner, char unload)
8756 {
8757 
8758     #define LOG_CMD_TITLE   "%-20s"
8759 
8760     s_model *newchar = NULL,
8761             *tempmodel = NULL;
8762 
8763     s_anim *newanim = NULL;
8764 
8765     char *filename      = NULL,
8766          *buf           = NULL,
8767          *animscriptbuf = NULL,
8768          *scriptbuf     = NULL,
8769          *command       = NULL,
8770          *value         = NULL,
8771          *value2        = NULL,
8772          *value3        = NULL;
8773 
8774     char fnbuf[MAX_BUFFER_LEN] = {""},
8775                       namebuf[MAX_BUFFER_LEN] = {""},
8776                                      argbuf[MAX_ARG_LEN + 1] = {""};
8777 
8778     ArgList arglist;
8779 
8780     float tempFloat;
8781 
8782     int ani_id = -1,
8783         script_id = -1,
8784         frm_id = -1,
8785         i = 0,
8786         j = 0,
8787         tempInt = 0,
8788         framecount = 0,
8789         //framenum = 0,
8790         frameset = 0,
8791         peek = 0,
8792         cacheindex = 0,
8793         curframe = 0,
8794         delay = 0,
8795         errorVal = 0,
8796         shadow_set = 0,
8797         idle = 0,
8798         frameshadow = -1,	// -1 will use default shadow for this entity, otherwise will use this value
8799         soundtoplay = -1,
8800         aimoveset = 0,
8801         aiattackset = 0,
8802         maskindex = -1,
8803         nopalette = 0;
8804 
8805     size_t size = 0,
8806            line = 0,
8807            len = 0,
8808            sbsize = 0,
8809            scriptlen = 0;
8810 
8811     ptrdiff_t pos = 0,
8812               index = 0;
8813 
8814     s_hitbox            bbox = {    .x      = 0,
8815                                     .y      = 0,
8816                                     .width  = 0,
8817                                     .height = 0,
8818                                     .z1     = 0,
8819                                     .z2     = 0};
8820 
8821     s_hitbox            ebox = {    .x      = 0,
8822                                     .y      = 0,
8823                                     .width  = 0,
8824                                     .height = 0,
8825                                     .z1     = 0,
8826                                     .z2     = 0};
8827 
8828     s_hitbox            abox = {    .x = 0,
8829                                     .y = 0,
8830                                     .width = 0,
8831                                     .height = 0,
8832                                     .z1 = 0,
8833                                     .z2 = 0};
8834 
8835     s_axis_plane_vertical_int         offset = { .x = 0,
8836                                                  .y = 0 };
8837     int                 shadow_xz[2] = {0, 0};
8838     int                 shadow_coords[2] = {0, 0};
8839 
8840     float               platform[8] = { 0, 0, 0, 0, 0, 0, 0, 0 },
8841                         platform_con[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
8842 
8843     s_move                  move = {    {   .x = 0,
8844                                             .y = 0,
8845                                             .z = 0},
8846                                         .base = -1    //-1 = Disabled, 0+ base set
8847                                     };
8848 
8849     s_damage_recursive  recursive;
8850     s_hitbox            attack_coords;
8851     s_collision_attack  attack;
8852     s_collision_attack  *pattack = NULL;
8853     s_collision_body    bbox_con;
8854     s_collision_entity  ebox_con;
8855     s_hitbox            body_coords;
8856     s_hitbox            entity_coords;
8857     s_defense           defense;
8858     s_drawmethod        drawmethod;
8859     s_drawmethod        dm;
8860 
8861     char *shutdownmessage = NULL;
8862 
8863 
8864 
8865     unsigned *mapflag = NULL;  // in 24bit mode, we need to know whether a colourmap is a common map or a palette
8866 
8867     static const char pre_text[] =   // this is the skeleton of frame function
8868     {
8869         "void main()\n"
8870         "{\n"
8871         "    int frame = getlocalvar(\"frame\");\n"
8872         "    int animhandle = getlocalvar(\"animhandle\");\n"
8873         "\n}\n"
8874     };
8875 
8876     static const char sur_text[] =  // end of function text
8877     {
8878         "\n}\n"
8879     };
8880 
8881     static const char ifid_text[] =  // if expression to check animation id
8882     {
8883         "    if(animhandle==%d)\n"
8884         "    {\n"
8885         "        return;\n"
8886         "    }\n"
8887     };
8888 
8889     static const char endifid_text[] =  // end of if
8890     {
8891         "        return;\n"
8892         "    }\n"
8893     };
8894 
8895     static const char if_text[] =  // this is the if expression of frame function
8896     {
8897         "        if(frame==%d)\n"
8898         "        {\n"
8899     };
8900 
8901     static const char endif_return_text[] =   //return to reduce unecessary checks
8902     {
8903         "            return;\n"
8904     };
8905 
8906     static const char endif_text[] =  // end of if
8907     {
8908         "        }\n"
8909     } ;
8910 
8911     static const char comma_text[] =  // arguments separator
8912     {
8913         ", "
8914     };
8915 
8916     static const char call_text[] =  //begin of function call
8917     {
8918         "            %s("
8919     };
8920 
8921     static const char endcall_text[] =  //end of function call
8922     {
8923         ");\n"
8924     };
8925 
8926     modelCommands cmd;
8927     s_scripts *tempscripts;
8928 
8929 #ifdef DEBUG
8930     printf("load_cached_model: %s, unload: %d\n", name, unload);
8931 #endif
8932 
8933     // Start up the standard log entry.
8934     printf("Loaded '%s'", name);
8935 
8936     // Model already loaded but we might want to unload after level is completed.
8937     if((tempmodel = findmodel(name)) != NULL)
8938     {
8939         update_model_loadflag(tempmodel, unload);
8940         cache_model_sprites(tempmodel, 1);
8941         return tempmodel;
8942     }
8943 
8944     cacheindex = get_cached_model_index(name);
8945     if(cacheindex < 0)
8946     {
8947         borShutdown(1, "Fatal: No cache entry for '%s' within '%s'\n\n", name, owner);
8948     }
8949 
8950     // Get the text file name of model.
8951     filename = model_cache[cacheindex].path;
8952     printf(" from %s \n", filename);
8953 
8954     if(buffer_pakfile(filename, &buf, &size) != 1)
8955     {
8956         borShutdown(1, "Unable to open file '%s'\n\n", filename);
8957     }
8958 
8959     sbsize = size + 1;
8960     scriptbuf = (char *)malloc(sbsize);
8961 
8962     if(scriptbuf == NULL)
8963     {
8964         borShutdown(1, "Unable to create script buffer for file '%s' (%i bytes)", filename, size * 2);
8965     }
8966     scriptbuf[0] = 0;
8967 
8968     //_peek_model_name(cacheindex);
8969     newchar = init_model(cacheindex, unload);
8970     //newchar->name = name;
8971 
8972     //attention, we increase models_loaded here, this can be dangerous if we access that value later on,
8973     //since recursive calls will change it!
8974     models_loaded++;
8975     addModel(newchar);
8976 
8977     attack = emptyattack;      // empty attack
8978 
8979 	attack.dropv = default_model_dropv;
8980 
8981     bbox_con = empty_body;
8982     ebox_con = empty_entity_collision;
8983 
8984     drawmethod = plainmethod;  // better than memset it to 0
8985 
8986     newchar->hitwalltype = -1; // init to -1
8987 
8988 
8989     //char* test = "load   knife 0";
8990     //ParseArgs(&arglist,test,argbuf);
8991 
8992     // Now interpret the contents of buf line by line
8993     while(pos < size)
8994     {
8995         //command = GET_ARG(0);
8996         line++;
8997         if(ParseArgs(&arglist, buf + pos, argbuf))
8998         {
8999             command = GET_ARG(0);
9000             cmd = getModelCommand(modelcmdlist, command);
9001 
9002             //if (cmd != CMD_MODEL_FRAME) framenum = 0;
9003 
9004             switch(cmd)
9005             {
9006             case CMD_MODEL_BACKPAIN:
9007                 value = GET_ARG(1);
9008                 newchar->backpain = atoi(value);
9009                 break;
9010             case CMD_MODEL_SUBCLASS:
9011                 //inherit everything from an existing, cached model
9012                 tempmodel = findmodel(GET_ARG(1));
9013                 if (!tempmodel)
9014                 {
9015                     shutdownmessage = "tried to subclass a non-existing/not previously loaded model!";
9016                     goto lCleanup;
9017                 }
9018                 tempscripts = newchar->scripts;
9019                 *newchar = *tempmodel;
9020                 newchar->scripts = tempscripts;
9021                 copy_all_scripts(tempmodel->scripts, newchar->scripts, 1);
9022                 newchar->isSubclassed = 1;
9023                 newchar->freetypes = MF_SCRIPTS;
9024                 break;
9025             case CMD_MODEL_NAME:
9026                 lcmHandleCommandName(&arglist, newchar, cacheindex);
9027                 break;
9028             case CMD_MODEL_TYPE:
9029                 lcmHandleCommandType(&arglist, newchar, filename);
9030                 break;
9031             case CMD_MODEL_SUBTYPE:
9032                 lcmHandleCommandSubtype(&arglist, newchar, filename);
9033                 break;
9034             case CMD_MODEL_HEALTH:
9035                 value = GET_ARG(1);
9036                 newchar->health = atoi(value);
9037                 break;
9038             case CMD_MODEL_PRIORITY:
9039                 value = GET_ARG(1);
9040                 newchar->priority = atoi(value);
9041                 break;
9042             case CMD_MODEL_SCROLL:
9043                 value = GET_ARG(1);
9044                 newchar->scroll = atof(value);
9045                 break;
9046             case CMD_MODEL_MP: //Left for backward compatability. See mpset. // mp values to put max mp for player by tails
9047                 value = GET_ARG(1);
9048                 newchar->mp = atoi(value);
9049                 break;
9050             case CMD_MODEL_NOLIFE:	// Feb 25, 2005 - Flag to display enemy life or not
9051                 newchar->nolife = GET_INT_ARG(1);
9052                 break;
9053             case CMD_MODEL_MAKEINV:	// Mar 12, 2005 - If a value is supplied, corresponds to amount of time the player spawns invincible
9054                 newchar->makeinv = GET_FLOAT_ARG(1) * GAME_SPEED;
9055                 if(GET_INT_ARG(2))
9056                 {
9057                     newchar->makeinv = -newchar->makeinv;
9058                 }
9059                 break;
9060             case CMD_MODEL_RISEINV:
9061                 newchar->riseinv = GET_FLOAT_ARG(1) * GAME_SPEED;
9062                 if(GET_INT_ARG(2))
9063                 {
9064                     newchar->riseinv = -newchar->riseinv;
9065                 }
9066                 break;
9067             case CMD_MODEL_LOAD:
9068                 value = GET_ARG(1);
9069                 tempmodel = findmodel(value);
9070                 if(!tempmodel)
9071                 {
9072                     load_cached_model(value, name, GET_INT_ARG(2));
9073                 }
9074                 else
9075                 {
9076                     update_model_loadflag(tempmodel, GET_INT_ARG(2));
9077                 }
9078                 break;
9079             case CMD_MODEL_SCORE:
9080                 newchar->score = GET_INT_ARG(1);
9081                 newchar->multiple = GET_INT_ARG(2);			// New var multiple for force/scoring
9082                 break;
9083             case CMD_MODEL_SMARTBOMB:
9084                 lcmHandleCommandSmartbomb(&arglist, newchar, filename);
9085                 break;
9086             case CMD_MODEL_BOUNCE:
9087                 newchar->bounce = GET_INT_ARG(1);
9088                 break;
9089             case CMD_MODEL_NOQUAKE:  // Mar 12, 2005 - Flag to determine if entity shakes screen
9090                 newchar->noquake |= GET_INT_ARG(1) ? NO_QUAKE : 0;
9091                 newchar->noquake |= GET_INT_ARG(2) ? NO_QUAKEN : 0;
9092                 break;
9093             case CMD_MODEL_BLOCKBACK:	// Flag to determine if attacks can be blocked from behind
9094                 newchar->blockback = GET_INT_ARG(1);
9095                 break;
9096             case CMD_MODEL_HITENEMY:	// Flag to determine if an enemy projectile will hit enemies
9097                 value = GET_ARG(1);
9098                 if(atoi(value) == 1)
9099                 {
9100                     newchar->candamage = newchar->hostile = TYPE_PLAYER | TYPE_ENEMY;
9101                 }
9102                 else if(atoi(value) == 2)
9103                 {
9104                     newchar->candamage = newchar->hostile = TYPE_PLAYER;
9105                 }
9106                 newchar->ground = GET_INT_ARG(2);    // Added to determine if enemies are damaged with mid air projectiles or ground only
9107                 break;
9108             case CMD_MODEL_HOSTILE:
9109                 lcmHandleCommandHostile(&arglist, newchar);
9110                 break;
9111             case CMD_MODEL_CANDAMAGE:
9112                 lcmHandleCommandCandamage(&arglist, newchar);
9113                 break;
9114             case CMD_MODEL_PROJECTILEHIT:
9115                 lcmHandleCommandProjectilehit(&arglist, newchar);
9116                 break;
9117             case CMD_MODEL_AIMOVE:
9118                 lcmHandleCommandAimove(&arglist, newchar, &aimoveset, filename);
9119                 break;
9120             case CMD_MODEL_AIATTACK:
9121                 lcmHandleCommandAiattack(&arglist, newchar, &aiattackset, filename);
9122                 break;
9123             case CMD_MODEL_SUBJECT_TO_BASEMAP:
9124                 newchar->subject_to_basemap = (0 != GET_INT_ARG(1));
9125                 break;
9126             case CMD_MODEL_SUBJECT_TO_WALL:
9127                 newchar->subject_to_wall = (0 != GET_INT_ARG(1));
9128                 break;
9129             case CMD_MODEL_SUBJECT_TO_HOLE:
9130                 newchar->subject_to_hole = (0 != GET_INT_ARG(1));
9131                 break;
9132             case CMD_MODEL_SUBJECT_TO_PLATFORM:
9133                 newchar->subject_to_platform = (0 != GET_INT_ARG(1));
9134                 break;
9135             case CMD_MODEL_SUBJECT_TO_OBSTACLE:
9136                 newchar->subject_to_obstacle = (0 != GET_INT_ARG(1));
9137                 break;
9138             case CMD_MODEL_SUBJECT_TO_GRAVITY:
9139                 newchar->subject_to_gravity = (0 != GET_INT_ARG(1));
9140                 break;
9141             case CMD_MODEL_SUBJECT_TO_SCREEN:
9142                 newchar->subject_to_screen = (0 != GET_INT_ARG(1));
9143                 break;
9144             case CMD_MODEL_SUBJECT_TO_MINZ:
9145                 newchar->subject_to_minz = (0 != GET_INT_ARG(1));
9146                 break;
9147             case CMD_MODEL_SUBJECT_TO_MAXZ:
9148                 newchar->subject_to_maxz = (0 != GET_INT_ARG(1));
9149                 break;
9150             case CMD_MODEL_NO_ADJUST_BASE:
9151                 newchar->no_adjust_base = (0 != GET_INT_ARG(1));
9152                 break;
9153             case CMD_MODEL_INSTANTITEMDEATH:
9154                 newchar->instantitemdeath = GET_INT_ARG(1);
9155                 break;
9156             case CMD_MODEL_SECRET:
9157                 newchar->secret = GET_INT_ARG(1);
9158                 newchar->clearcount = GET_INT_ARG(2);
9159                 break;
9160             case CMD_MODEL_MODELFLAG: //model copy flag
9161                 newchar->model_flag = GET_INT_ARG(1);
9162                 break;
9163                 // weapons
9164             case CMD_MODEL_WEAPLOSS:
9165                 newchar->weaploss[0] = GET_INT_ARG(1);
9166                 newchar->weaploss[1] = GET_INT_ARG(2);
9167                 break;
9168             case CMD_MODEL_WEAPNUM:
9169                 newchar->weapnum = GET_INT_ARG(1);
9170                 break;
9171             case CMD_MODEL_PROJECT: // New projectile subtype
9172                 value = GET_ARG(1);
9173                 if(stricmp(value, "none") == 0)
9174                 {
9175                     newchar->project = -1;
9176                 }
9177                 else
9178                 {
9179                     newchar->project = get_cached_model_index(value);
9180                 }
9181                 break;
9182             case CMD_MODEL_WEAPONS:
9183                 lcmHandleCommandWeapons(&arglist, newchar);
9184                 break;
9185             case CMD_MODEL_SHOOTNUM: //here weapons things like shoot rest type of weapon ect..by tails
9186                 newchar->shootnum = GET_INT_ARG(1);
9187                 break;
9188             case CMD_MODEL_RELOAD:
9189                 newchar->reload = GET_INT_ARG(1);
9190                 break;
9191             case CMD_MODEL_TYPESHOT:
9192                 newchar->typeshot = GET_INT_ARG(1);
9193                 break;
9194             case CMD_MODEL_COUNTER:
9195                 newchar->counter = GET_INT_ARG(1);
9196                 break;
9197             case CMD_MODEL_ANIMAL:
9198                 newchar->animal = GET_INT_ARG(1);
9199                 break;
9200             case CMD_MODEL_RIDER:
9201                 value = GET_ARG(1);
9202                 if(stricmp(value, "none") == 0)
9203                 {
9204                     newchar->rider = -1;
9205                 }
9206                 else
9207                 {
9208                     newchar->rider = get_cached_model_index(value);
9209                 }
9210                 break;
9211             case CMD_MODEL_KNIFE:
9212             case CMD_MODEL_FIREB:
9213             case CMD_MODEL_PLAYSHOT:
9214             case CMD_MODEL_PLAYSHOTW:
9215                 value = GET_ARG(1);
9216                 if(stricmp(value, "none") == 0)
9217                 {
9218                     newchar->knife = -1;
9219                 }
9220                 else
9221                 {
9222                     newchar->knife = get_cached_model_index(value);
9223                 }
9224                 break;
9225             case CMD_MODEL_PLAYSHOTNO:
9226                 value = GET_ARG(1);
9227                 if(stricmp(value, "none") == 0)
9228                 {
9229                     newchar->pshotno = -1;
9230                 }
9231                 else
9232                 {
9233                     newchar->pshotno = get_cached_model_index(value);
9234                 }
9235                 break;
9236             case CMD_MODEL_STAR:
9237                 value = GET_ARG(1);
9238                 if(stricmp(value, "none") == 0)
9239                 {
9240                     newchar->star = -1;
9241                 }
9242                 else
9243                 {
9244                     newchar->star = get_cached_model_index(value);
9245                 }
9246                 break;
9247             case CMD_MODEL_BOMB:
9248             case CMD_MODEL_PLAYBOMB:
9249                 value = GET_ARG(1);
9250                 if(stricmp(value, "none") == 0)
9251                 {
9252                     newchar->bomb = -1;
9253                 }
9254                 else
9255                 {
9256                     newchar->bomb = get_cached_model_index(value);
9257                 }
9258                 break;
9259             case CMD_MODEL_FLASH:	 // Now all characters can have their own flash - even projectiles (useful for blood)
9260                 value = GET_ARG(1);
9261                 if(stricmp(value, "none") == 0)
9262                 {
9263                     newchar->flash = -1;
9264                 }
9265                 else
9266                 {
9267                     newchar->flash = get_cached_model_index(value);
9268                 }
9269                 break;
9270             case CMD_MODEL_BFLASH:	// Flash that is spawned if an attack is blocked
9271                 value = GET_ARG(1);
9272                 if(stricmp(value, "none") == 0)
9273                 {
9274                     newchar->bflash = -1;
9275                 }
9276                 else
9277                 {
9278                     newchar->bflash = get_cached_model_index(value);
9279                 }
9280                 break;
9281             case CMD_MODEL_DUST:	// Spawned when hitting the ground to "kick up dust"
9282                 value = GET_ARG(1);
9283                 if(stricmp(value, "none") == 0)
9284                 {
9285                     newchar->dust.fall_land = -1;
9286                 }
9287                 else
9288                 {
9289                     newchar->dust.fall_land = get_cached_model_index(value);
9290                 }
9291                 value = GET_ARG(2);
9292                 if(stricmp(value, "none") == 0)
9293                 {
9294                     newchar->dust.jump_land = -1;
9295                 }
9296                 else
9297                 {
9298                     newchar->dust.jump_land = get_cached_model_index(value);
9299                 }
9300                 value = GET_ARG(3);
9301                 if(stricmp(value, "none") == 0)
9302                 {
9303                     newchar->dust.jump_start = -1;
9304                 }
9305                 else
9306                 {
9307                     newchar->dust.jump_start = get_cached_model_index(value);
9308                 }
9309                 break;
9310             case CMD_MODEL_BRANCH: // for endlevel item's level branch
9311                 value = GET_ARG(1);
9312                 if(!newchar->branch)
9313                 {
9314                     newchar->branch = malloc(MAX_NAME_LEN + 1);
9315                     newchar->branch[0] = 0;
9316                 }
9317                 strncpy(newchar->branch, value, MAX_NAME_LEN);
9318                 break;
9319             case CMD_MODEL_CANTGRAB:
9320             case CMD_MODEL_NOTGRAB:
9321                 tempInt = GET_INT_ARG(1);
9322                 if(tempInt == 2)
9323                 {
9324                     newchar->grabforce = -999999;
9325                 }
9326                 else
9327                 {
9328                     newchar->antigrab = 1;
9329                 }
9330                 break;
9331             case CMD_MODEL_ANTIGRAB: // a can grab b: a->antigrab - b->grabforce <=0
9332                 newchar->antigrab = GET_INT_ARG(1);
9333                 break;
9334             case CMD_MODEL_GRABFORCE:
9335                 newchar->grabforce = GET_INT_ARG(1);
9336                 break;
9337             case CMD_MODEL_GRABBACK:
9338                 newchar->grabback = GET_INT_ARG(1);
9339                 break;
9340             case CMD_MODEL_OFFSCREENKILL:
9341                 newchar->offscreenkill = GET_INT_ARG(1);
9342                 break;
9343             case CMD_MODEL_ONAF:
9344                 newchar->offscreen_noatk_factor = GET_FLOAT_ARG(1);
9345                 break;
9346             case CMD_MODEL_FALLDIE:
9347             case CMD_MODEL_DEATH:
9348                 newchar->falldie = GET_INT_ARG(1);
9349                 break;
9350             case CMD_MODEL_SPEED:
9351                 value = GET_ARG(1);
9352                 newchar->speed = atof(value);
9353                 newchar->speed /= 10;
9354                 if(newchar->speed < 0.5)
9355                 {
9356                     newchar->speed = 0.5;
9357                 }
9358                 if(newchar->speed > 30)
9359                 {
9360                     newchar->speed = 30;
9361                 }
9362                 break;
9363             case CMD_MODEL_SPEEDF:
9364                 value = GET_ARG(1);
9365                 newchar->speed = atof(value);
9366                 break;
9367             case CMD_MODEL_JUMPSPEED:
9368                 value = GET_ARG(1);
9369                 newchar->jumpspeed = atof(value);
9370                 newchar->jumpspeed /= 10;
9371                 break;
9372             case CMD_MODEL_JUMPSPEEDF:
9373                 value = GET_ARG(1);
9374                 newchar->jumpspeed = atof(value);
9375                 break;
9376             case CMD_MODEL_ANTIGRAVITY:
9377                 value = GET_ARG(1);
9378                 newchar->antigravity = atof(value);
9379                 newchar->antigravity /= 100;
9380                 break;
9381             case CMD_MODEL_STEALTH:
9382                 newchar->stealth.hide = GET_INT_ARG(1);
9383                 newchar->stealth.detect = GET_INT_ARG(2);
9384                 break;
9385             case CMD_MODEL_JUGGLEPOINTS:
9386                 value = GET_ARG(1);
9387                 newchar->jugglepoints.current = atoi(value);
9388                 newchar->jugglepoints.max = atoi(value);
9389                 break;
9390             case CMD_MODEL_RISEATTACKTYPE:
9391                 value = GET_ARG(1);
9392                 newchar->riseattacktype = atoi(value);
9393                 break;
9394             case CMD_MODEL_GUARDPOINTS:
9395                 value = GET_ARG(1);
9396                 newchar->guardpoints.current = atoi(value);
9397                 newchar->guardpoints.max = atoi(value);
9398                 break;
9399             case CMD_MODEL_DEFENSE:
9400 #define tempdef(x, y) \
9401 					x(stricmp(value, #y)==0)\
9402 					{\
9403 						newchar->defense[ATK_##y] = defense;\
9404 					}
9405             {
9406                 value = GET_ARG(1);
9407                 defense = default_defense;
9408                 if(newchar->subtype == SUBTYPE_BIKER)
9409                 {
9410                     defense.factor = 2.f;
9411                 }
9412 
9413                 if(arglist.count >= 2)
9414                 {
9415                     defense.factor = GET_FLOAT_ARG(2);
9416                 }
9417                 if(arglist.count >= 3)
9418                 {
9419                     defense.pain = GET_FLOAT_ARG(3);
9420                 }
9421                 if(arglist.count >= 4)
9422                 {
9423                     defense.knockdown = GET_FLOAT_ARG(4);
9424                 }
9425                 if(arglist.count >= 5)
9426                 {
9427                     defense.blockpower = GET_FLOAT_ARG(5);
9428                 }
9429                 if(arglist.count >= 6)
9430                 {
9431                     defense.blockthreshold = GET_FLOAT_ARG(6);
9432                 }
9433                 if(arglist.count >= 7)
9434                 {
9435                     defense.blockratio = GET_FLOAT_ARG(7);
9436                 }
9437                 if(arglist.count >= 8)
9438                 {
9439                     defense.blocktype = GET_FLOAT_ARG(8);
9440                 }
9441 
9442                 tempdef(if, NORMAL)
9443                 tempdef(else if, NORMAL2)
9444                 tempdef(else if, NORMAL3)
9445                 tempdef(else if, NORMAL4)
9446                 tempdef(else if, NORMAL5)
9447                 tempdef(else if, NORMAL6)
9448                 tempdef(else if, NORMAL7)
9449                 tempdef(else if, NORMAL8)
9450                 tempdef(else if, NORMAL9)
9451                 tempdef(else if, NORMAL10)
9452                 tempdef(else if, BLAST)
9453                 tempdef(else if, STEAL)
9454                 tempdef(else if, BURN)
9455                 tempdef(else if, SHOCK)
9456                 tempdef(else if, FREEZE)
9457 
9458                 tempdef(else if, BOSS_DEATH)
9459                 tempdef(else if, ITEM)
9460                 tempdef(else if, LAND)
9461                 tempdef(else if, LIFESPAN)
9462                 tempdef(else if, LOSE)
9463                 tempdef(else if, PIT)
9464                 tempdef(else if, TIMEOVER)
9465 
9466                 else if(starts_with(value, "normal"))
9467                 {
9468                     get_tail_number(tempInt, value, "normal");
9469                     newchar->defense[tempInt + STA_ATKS - 1] = defense;
9470                 }
9471                 else if(stricmp(value, "ALL") == 0)
9472                 {
9473                     // Loop over all attack types and apply
9474                     // the value setting.
9475                     for(i = 0; i < max_attack_types; i++)
9476                     {
9477                         // Skip types that we only intend for
9478                         // engine or script logic use.
9479                         if(i == ATK_BOSS_DEATH
9480                            || i == ATK_ITEM
9481                            || i == ATK_LIFESPAN
9482                            || i == ATK_LOSE
9483                            || i == ATK_TIMEOVER
9484                            || i == ATK_PIT)
9485                         {
9486                             continue;
9487                         }
9488 
9489                         newchar->defense[i] = defense;
9490 
9491                     }
9492                 }
9493             }
9494 #undef tempdef
9495             break;
9496             case CMD_MODEL_OFFENSE:
9497 #define tempoff(x, y, z) \
9498 					x(stricmp(value, #y)==0)\
9499 					{\
9500 					newchar->z[ATK_##y] = GET_FLOAT_ARG(2);\
9501 					/*newchar->z[ATK_##y] /= 100;*/\
9502 					}
9503             {
9504                 value = GET_ARG(1);
9505                 tempoff(if,         NORMAL,     offense_factors)
9506                 tempoff(else if,    NORMAL2,    offense_factors)
9507                 tempoff(else if,    NORMAL3,    offense_factors)
9508                 tempoff(else if,    NORMAL4,    offense_factors)
9509                 tempoff(else if,    NORMAL5,    offense_factors)
9510                 tempoff(else if,    NORMAL6,    offense_factors)
9511                 tempoff(else if,    NORMAL7,    offense_factors)
9512                 tempoff(else if,    NORMAL8,    offense_factors)
9513                 tempoff(else if,    NORMAL9,    offense_factors)
9514                 tempoff(else if,    NORMAL10,   offense_factors)
9515                 tempoff(else if,    BLAST,      offense_factors)
9516                 tempoff(else if,    STEAL,      offense_factors)
9517                 tempoff(else if,    BURN,       offense_factors)
9518                 tempoff(else if,    SHOCK,      offense_factors)
9519                 tempoff(else if,    FREEZE,     offense_factors)
9520 
9521                 tempoff(else if,    BOSS_DEATH, offense_factors)
9522                 tempoff(else if,    ITEM,       offense_factors)
9523                 tempoff(else if,    LAND,       offense_factors)
9524                 tempoff(else if,    LIFESPAN,   offense_factors)
9525                 tempoff(else if,    LOSE,       offense_factors)
9526                 tempoff(else if,    PIT,        offense_factors)
9527                 tempoff(else if,    TIMEOVER,   offense_factors)
9528 
9529                 else if(starts_with(value, "normal"))
9530                 {
9531                     get_tail_number(tempInt, value, "normal");
9532                     newchar->offense_factors[tempInt + STA_ATKS - 1] = GET_FLOAT_ARG(2);
9533                 }
9534                 else if(stricmp(value, "ALL") == 0)
9535                 {
9536                     tempFloat = GET_FLOAT_ARG(2);
9537 
9538                     // Loop over all attack types and apply
9539                     // the value setting.
9540                     for(i = 0; i < max_attack_types; i++)
9541                     {
9542                         // Skip types that we only intend for
9543                         // engine or script logic use.
9544                         if(i == ATK_BOSS_DEATH
9545                            || i == ATK_ITEM
9546                            || i == ATK_LIFESPAN
9547                            || i == ATK_LOSE
9548                            || i == ATK_TIMEOVER
9549                            || i == ATK_PIT)
9550                         {
9551                             continue;
9552                         }
9553 
9554                         newchar->offense_factors[i] = tempFloat;
9555                     }
9556                 }
9557             }
9558 #undef tempoff
9559             break;
9560             case CMD_MODEL_HEIGHT:
9561                 newchar->size.y = GET_INT_ARG(1);
9562                 break;
9563             case CMD_MODEL_JUMPHEIGHT:
9564                 newchar->jumpheight = GET_FLOAT_ARG(1);
9565                 break;
9566             case CMD_MODEL_JUMPMOVE:
9567                 newchar->jumpmovex = GET_INT_ARG(1);
9568                 newchar->jumpmovez = GET_INT_ARG(2);
9569                 break;
9570             case CMD_MODEL_WALKOFFMOVE:
9571                 newchar->walkoffmovex = GET_INT_ARG(1);
9572                 newchar->walkoffmovez = GET_INT_ARG(2);
9573                 break;
9574             case CMD_MODEL_KNOCKDOWNCOUNT:
9575                 newchar->knockdowncount = GET_FLOAT_ARG(1);
9576                 break;
9577             case CMD_MODEL_GRABDISTANCE:
9578                 newchar->grabdistance = GET_FLOAT_ARG(1);                    // 30-12-2004 and store for character
9579                 break;
9580             case CMD_MODEL_GRABFLIP:
9581                 newchar->grabflip = GET_INT_ARG(1);
9582                 break;
9583             case CMD_MODEL_GRABFINISH:
9584                 newchar->grabfinish = GET_INT_ARG(1);
9585                 break;
9586             case CMD_MODEL_THROWDAMAGE:
9587                 newchar->throwdamage = GET_INT_ARG(1);
9588                 break;
9589             case CMD_MODEL_SHADOW:
9590                 newchar->shadow = GET_INT_ARG(1);
9591                 newchar->shadowbase = GET_INT_ARG(2);
9592                 break;
9593             case CMD_MODEL_GFXSHADOW:
9594                 newchar->gfxshadow = GET_INT_ARG(1);
9595                 newchar->shadowbase = GET_INT_ARG(2);
9596                 break;
9597             case CMD_MODEL_AIRONLY:	// Shadows display in air only?
9598                 newchar->aironly = GET_INT_ARG(1);
9599                 break;
9600             case CMD_MODEL_FMAP:	// Map that corresponds with the remap when a character is frozen
9601                 newchar->maps.frozen = GET_INT_ARG(1);
9602                 break;
9603             case CMD_MODEL_KOMAP:	// Remap when character is KO'd.
9604                 newchar->maps.ko = GET_INT_ARG(1);  //Remap.
9605                 newchar->maps.kotype = GET_INT_ARG(2);  //Type: 0 start of fall/death, 1 last frame.
9606                 break;
9607             case CMD_MODEL_HMAP:	// Maps range unavailable to player in select screen.
9608                 newchar->maps.hide_start = GET_INT_ARG(1); //First unavailable map.
9609                 newchar->maps.hide_end = GET_INT_ARG(2); //Last unavailable map.
9610                 break;
9611             case CMD_MODEL_SETLAYER:
9612                 newchar->setlayer = GET_INT_ARG(1);
9613                 break;
9614             case CMD_MODEL_TOFLIP:	  // Flag to determine if flashes images will be flipped or not
9615                 newchar->toflip = GET_INT_ARG(1);
9616                 break;
9617             case CMD_MODEL_NODIEBLINK:
9618                 // Added to determine if dying animation blinks or not
9619                 newchar->nodieblink = GET_INT_ARG(1);
9620                 break;
9621             case CMD_MODEL_NOATFLASH:	 // Flag to determine if an opponents attack spawns their flash or not
9622                 newchar->noatflash = GET_INT_ARG(1);
9623                 break;
9624             case CMD_MODEL_NOMOVE:
9625                 // If set, will be static (speed must be set to 0 or left blank)
9626                 newchar->nomove = GET_INT_ARG(1);
9627                 newchar->noflip = GET_INT_ARG(2);    // If set, static will not flip directions
9628                 if(newchar->nomove)
9629                 {
9630                     newchar->nodrop = 1;
9631                 }
9632                 break;
9633             case CMD_MODEL_NODROP:
9634                 newchar->nodrop = GET_INT_ARG(1);
9635                 break;
9636             case CMD_MODEL_THOLD:
9637                 // Threshold for enemies/players block
9638                 newchar->thold = GET_INT_ARG(1);
9639                 break;
9640             case CMD_MODEL_RUNNING:
9641                 // The speed at which the player runs
9642                 newchar->runspeed = GET_FLOAT_ARG(1);
9643                 newchar->runspeed /= 10;
9644                 newchar->runjumpheight = GET_FLOAT_ARG(2);    // The height at which a player jumps when running
9645                 newchar->runjumpdist = GET_FLOAT_ARG(3);    // The distance a player jumps when running
9646                 newchar->runupdown = GET_INT_ARG(4);
9647                 newchar->runhold = GET_INT_ARG(5);
9648                 break;
9649             case CMD_MODEL_RUNNING_CONTINUE:
9650                 newchar->runhold = GET_FLOAT_ARG(1);
9651                 break;
9652             case CMD_MODEL_RUNNING_JUMP_VELOCITY_X:
9653                 newchar->runjumpdist = GET_FLOAT_ARG(1);
9654                 break;
9655             case CMD_MODEL_RUNNING_JUMP_VELOCITY_Y:
9656                 newchar->runjumpheight = GET_FLOAT_ARG(1);
9657                 break;
9658             case CMD_MODEL_RUNNING_SPEED:
9659                 newchar->runspeed = GET_FLOAT_ARG(1);
9660                 break;
9661             case CMD_MODEL_RUNNING_Z_MOVE:
9662                 newchar->runupdown = GET_FLOAT_ARG(1);
9663                 break;
9664             case CMD_MODEL_BLOCKODDS:
9665                 // Odds that an attack will hit an enemy (1 : blockodds)
9666                 newchar->blockodds = GET_INT_ARG(1);
9667                 break;
9668             case CMD_MODEL_HOLDBLOCK:
9669                 newchar->holdblock = GET_INT_ARG(1);
9670                 break;
9671             case CMD_MODEL_BLOCKPAIN:
9672                 newchar->blockpain = GET_INT_ARG(1);
9673                 break;
9674             case CMD_MODEL_NOPASSIVEBLOCK:
9675                 newchar->nopassiveblock = GET_INT_ARG(1);
9676                 break;
9677             case CMD_MODEL_EDELAY:
9678                 newchar->edelay.mode        = GET_INT_ARG(1);
9679                 newchar->edelay.factor      = GET_FLOAT_ARG(2);
9680                 newchar->edelay.cap.min     = GET_INT_ARG(3);
9681                 newchar->edelay.cap.max     = GET_INT_ARG(4);
9682                 newchar->edelay.range.min   = GET_INT_ARG(5);
9683                 newchar->edelay.range.max   = GET_INT_ARG(6);
9684                 break;
9685             case CMD_MODEL_PAINGRAB:
9686                 newchar->paingrab = GET_INT_ARG(1);
9687                 break;
9688             case CMD_MODEL_THROW:
9689                 newchar->throwdist = GET_FLOAT_ARG(1);
9690                 newchar->throwheight = GET_FLOAT_ARG(2);
9691                 break;
9692             case CMD_MODEL_EDGERANGE:
9693                 newchar->edgerange.x = GET_FLOAT_ARG(1);
9694                 newchar->edgerange.z = GET_FLOAT_ARG(2);
9695                 break;
9696             case CMD_MODEL_ENTITYPUSHING:
9697                 newchar->entitypushing = GET_INT_ARG(1);
9698                 break;
9699             case CMD_MODEL_PUSHINGFACTOR:
9700                 newchar->pushingfactor = GET_FLOAT_ARG(1);
9701                 break;
9702             case CMD_MODEL_GRABWALK:
9703                 newchar->grabwalkspeed = GET_FLOAT_ARG(1);
9704                 newchar->grabwalkspeed /= 10;
9705                 if(newchar->grabwalkspeed < 0.5)
9706                 {
9707                     newchar->grabwalkspeed = 0.5;
9708                 }
9709                 break;
9710             case CMD_MODEL_GRABTURN:
9711                 newchar->grabturn = GET_INT_ARG(1);
9712                 break;
9713             case CMD_MODEL_THROWFRAMEWAIT:
9714                 newchar->throwframewait = GET_INT_ARG(1);
9715                 break;
9716             case CMD_MODEL_DIESOUND:
9717                 newchar->diesound = sound_load_sample(GET_ARG(1), packfile, 1);
9718                 break;
9719             case CMD_MODEL_ICON:
9720                 value = GET_ARG(1);
9721                 if(newchar->icon.def > -1)
9722                 {
9723                     shutdownmessage = "model has multiple icons defined";
9724                     goto lCleanup;
9725                 }
9726                 newchar->icon.def = loadsprite(value, 0, 0, pixelformat); //use same palette as the owner
9727                 newchar->icon.pain = newchar->icon.def;
9728                 newchar->icon.die = newchar->icon.def;
9729                 newchar->icon.get = newchar->icon.def;
9730                 newchar->icon.usemap = GET_INT_ARG(2); //be more friendly to some old mods which don't care about icon remap
9731                 break;
9732             case CMD_MODEL_ICONPAIN:
9733                 value = GET_ARG(1);
9734                 newchar->icon.pain = loadsprite(value, 0, 0, pixelformat);
9735                 break;
9736             case CMD_MODEL_ICONDIE:
9737                 value = GET_ARG(1);
9738                 newchar->icon.die = loadsprite(value, 0, 0, pixelformat);
9739                 break;
9740             case CMD_MODEL_ICONGET:
9741                 value = GET_ARG(1);
9742                 newchar->icon.get = loadsprite(value, 0, 0, pixelformat);
9743                 break;
9744             case CMD_MODEL_ICONW:
9745                 value = GET_ARG(1);
9746                 newchar->icon.weapon = loadsprite(value, 0, 0, pixelformat);
9747                 break;
9748             case CMD_MODEL_ICONMPHIGH:
9749                 value = GET_ARG(1);
9750                 newchar->icon.mphigh = loadsprite(value, 0, 0, pixelformat);
9751                 break;
9752             case CMD_MODEL_ICONMPHALF:
9753                 value = GET_ARG(1);
9754                 newchar->icon.mpmed = loadsprite(value, 0, 0, pixelformat);
9755                 break;
9756             case CMD_MODEL_ICONMPLOW:
9757                 value = GET_ARG(1);
9758                 newchar->icon.mplow = loadsprite(value, 0, 0, pixelformat);
9759                 break;
9760             case CMD_MODEL_PARROW:
9761                 // Image that is displayed when player 1 spawns invincible
9762                 value = GET_ARG(1);
9763                 newchar->parrow[0][0] = loadsprite(value, 0, 0, pixelformat);
9764                 newchar->parrow[0][1] = GET_INT_ARG(2);
9765                 newchar->parrow[0][2] = GET_INT_ARG(3);
9766                 break;
9767             case CMD_MODEL_PARROW2:
9768                 // Image that is displayed when player 2 spawns invincible
9769                 value = GET_ARG(1);
9770                 newchar->parrow[1][0] = loadsprite(value, 0, 0, pixelformat);
9771                 newchar->parrow[1][1] = GET_INT_ARG(2);
9772                 newchar->parrow[1][2] = GET_INT_ARG(3);
9773                 break;
9774             case CMD_MODEL_PARROW3:
9775                 value = GET_ARG(1);
9776                 newchar->parrow[2][0] = loadsprite(value, 0, 0, pixelformat);
9777                 newchar->parrow[2][1] = GET_INT_ARG(2);
9778                 newchar->parrow[2][2] = GET_INT_ARG(3);
9779                 break;
9780             case CMD_MODEL_PARROW4:
9781                 value = GET_ARG(1);
9782                 newchar->parrow[3][0] = loadsprite(value, 0, 0, pixelformat);
9783                 newchar->parrow[3][1] = GET_INT_ARG(2);
9784                 newchar->parrow[3][2] = GET_INT_ARG(3);
9785                 break;
9786             case CMD_MODEL_ATCHAIN:
9787                 newchar->chainlength = 0;
9788                 for(i = 0; i < MAX_ATCHAIN; i++)
9789                 {
9790                     newchar->atchain[i] = GET_INT_ARG(i + 1);
9791                     if(newchar->atchain[i] < 0)
9792                     {
9793                         newchar->atchain[i] = 0;
9794                     }
9795                     if(newchar->atchain[i] > max_attacks)
9796                     {
9797                         newchar->atchain[i] = max_attacks;
9798                     }
9799                     if(newchar->atchain[i])
9800                     {
9801                         newchar->chainlength = i + 1;
9802                     }
9803                 }
9804                 break;
9805             case CMD_MODEL_COMBOSTYLE:
9806                 newchar->combostyle = GET_INT_ARG(1);
9807                 break;
9808             case CMD_MODEL_CREDIT:
9809                 newchar->credit = GET_INT_ARG(1);
9810                 break;
9811             case CMD_MODEL_NOPAIN:
9812                 newchar->nopain = GET_INT_ARG(1);
9813                 break;
9814             case CMD_MODEL_ESCAPEHITS:
9815                 // How many times an enemy can be hit before retaliating
9816                 newchar->escapehits = GET_INT_ARG(1);
9817                 break;
9818             case CMD_MODEL_CHARGERATE:
9819                 // How much mp does this character gain while recharging?
9820                 newchar->chargerate = GET_INT_ARG(1);
9821                 break;
9822             case CMD_MODEL_MPRATE:
9823                 newchar->mprate = GET_INT_ARG(1);
9824                 break;
9825             case CMD_MODEL_MPSET:
9826                 // Mp bar wax/wane.
9827                 newchar->mp             = GET_INT_ARG(1); //Max MP.
9828                 newchar->mpstable       = GET_INT_ARG(2); //MP stable setting.
9829                 newchar->mpstableval    = GET_INT_ARG(3); //MP stable value (% Mp bar will try and maintain).
9830                 newchar->mprate         = GET_INT_ARG(4); //Rate MP value rises over time.
9831                 newchar->mpdroprate     = GET_INT_ARG(5); //Rate MP value drops over time.
9832                 newchar->chargerate     = GET_INT_ARG(6); //MP Chargerate.
9833                 break;
9834             case CMD_MODEL_SLEEPWAIT:
9835                 newchar->sleepwait = GET_INT_ARG(1);
9836                 break;
9837             case CMD_MODEL_GUARDRATE:
9838                 newchar->guardrate = GET_INT_ARG(1);
9839                 break;
9840             case CMD_MODEL_AGGRESSION:
9841                 newchar->aggression = GET_INT_ARG(1);
9842                 break;
9843             case CMD_MODEL_ATTACKTHROTTLE:
9844                 newchar->attackthrottle = GET_FLOAT_ARG(1);
9845                 if(arglist.count >= 2)
9846                 {
9847                     newchar->attackthrottletime = GET_FLOAT_ARG(2) * GAME_SPEED;
9848                 }
9849                 break;
9850             case CMD_MODEL_RISETIME:
9851                 newchar->risetime.rise = GET_INT_ARG(1);
9852                 newchar->risetime.riseattack = GET_INT_ARG(2);
9853                 break;
9854             case CMD_MODEL_FACING:
9855                 newchar->facing = GET_INT_ARG(1);
9856                 break;
9857             case CMD_MODEL_TURNDELAY:
9858                 newchar->turndelay = GET_INT_ARG(1);
9859                 break;
9860             case CMD_MODEL_LIFESPAN:
9861                 newchar->lifespan = GET_FLOAT_ARG(1) * GAME_SPEED;
9862                 break;
9863             case CMD_MODEL_SUMMONKILL:
9864                 newchar->summonkill = GET_INT_ARG(1);
9865                 break;
9866             case CMD_MODEL_LIFEPOSITION:
9867                 if((value = GET_ARG(1))[0])
9868                 {
9869                     newchar->hpx = atoi(value);
9870                 }
9871                 if((value = GET_ARG(2))[0])
9872                 {
9873                     newchar->hpy = atoi(value);
9874                 }
9875                 break;
9876             case CMD_MODEL_LIFEBARSTATUS:
9877                 _readbarstatus(buf + pos, &(newchar->hpbarstatus));
9878                 newchar->hpbarstatus.colourtable = &hpcolourtable;
9879                 break;
9880             case CMD_MODEL_ICONPOSITION:
9881                 if((value = GET_ARG(1))[0])
9882                 {
9883                     newchar->icon.position.x = atoi(value);
9884                 }
9885                 if((value = GET_ARG(2))[0])
9886                 {
9887                     newchar->icon.position.y = atoi(value);
9888                 }
9889                 break;
9890             case CMD_MODEL_NAMEPOSITION:
9891                 if((value = GET_ARG(1))[0])
9892                 {
9893                     newchar->namex = atoi(value);
9894                 }
9895                 if((value = GET_ARG(2))[0])
9896                 {
9897                     newchar->namey = atoi(value);
9898                 }
9899                 break;
9900             case CMD_MODEL_COM:
9901             {
9902                 // Section for custom freespecials starts here
9903                 int i, t;
9904                 int add_flag = 0;
9905                 alloc_specials(newchar);
9906                 newchar->special[newchar->specials_loaded].numkeys = 0;
9907                 for(i = 0, t = 1; i < MAX_SPECIAL_INPUTS - 3; i++, t++)
9908                 {
9909                     value = GET_ARG(t);
9910                     if(!value[0])
9911                     {
9912                         break;
9913                     }
9914                     if(stricmp(value, "u") == 0)
9915                     {
9916                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEUP;
9917                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEUP;
9918                         ++newchar->special[newchar->specials_loaded].numkeys;
9919                     }
9920                     else if(stricmp(value, "d") == 0)
9921                     {
9922                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEDOWN;
9923                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEDOWN;
9924                         ++newchar->special[newchar->specials_loaded].numkeys;
9925                     }
9926                     else if(stricmp(value, "f") == 0)
9927                     {
9928                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_FORWARD;
9929                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_FORWARD;
9930                         ++newchar->special[newchar->specials_loaded].numkeys;
9931                     }
9932                     else if(stricmp(value, "b") == 0)
9933                     {
9934                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_BACKWARD;
9935                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_BACKWARD;
9936                         ++newchar->special[newchar->specials_loaded].numkeys;
9937                     }
9938                     else if(stricmp(value, "a") == 0 || stricmp(value, "a1") == 0)
9939                     {
9940                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK;
9941                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK;
9942                         ++newchar->special[newchar->specials_loaded].numkeys;
9943                     }
9944                     else if(stricmp(value, "a2") == 0)
9945                     {
9946                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK2;
9947                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK2;
9948                         ++newchar->special[newchar->specials_loaded].numkeys;
9949                     }
9950                     else if(stricmp(value, "a3") == 0)
9951                     {
9952                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK3;
9953                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK3;
9954                         ++newchar->special[newchar->specials_loaded].numkeys;
9955                     }
9956                     else if(stricmp(value, "a4") == 0)
9957                     {
9958                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK4;
9959                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK4;
9960                         ++newchar->special[newchar->specials_loaded].numkeys;
9961                     }
9962                     else if(stricmp(value, "j") == 0)
9963                     {
9964                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_JUMP;
9965                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_JUMP;
9966                         ++newchar->special[newchar->specials_loaded].numkeys;
9967                     }
9968                     else if(stricmp(value, "s") == 0 || stricmp(value, "k") == 0)
9969                     {
9970                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_SPECIAL;
9971                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_SPECIAL;
9972                         ++newchar->special[newchar->specials_loaded].numkeys;
9973                     }
9974                     else if(starts_with_num(value, "freespecial"))
9975                     {
9976                         tempInt = atoi(value + 11);
9977                         if(tempInt < 1)
9978                         {
9979                             tempInt = 1;
9980                         }
9981                         newchar->special[newchar->specials_loaded].anim = animspecials[tempInt - 1];
9982                     }
9983                     else if(stricmp(value, "+") == 0 && i >= 1)
9984                     {
9985                         add_flag = 1;
9986                         i -= 2;
9987                         continue;
9988                     }
9989                     else if(stricmp(value, "->") == 0 && i > 0)
9990                     {
9991                         // just for better reading
9992                         --i;
9993                         continue;
9994                     }
9995                     else
9996                     {
9997                         shutdownmessage = "Invalid freespecial command";
9998                         goto lCleanup;
9999                     }
10000                     add_flag = 0;
10001                     //printf("insert:%s in %d, numkeys:%d for special n.:%d\n",value,i,newchar->special[newchar->specials_loaded].numkeys,newchar->specials_loaded);
10002                 }
10003                 newchar->special[newchar->specials_loaded].steps = i - 1; // max steps
10004                 newchar->specials_loaded++;
10005             }
10006             // End section for custom freespecials
10007             break;
10008             case CMD_MODEL_REMAP:
10009             {
10010                 // This command should not be used under 24bit mode, but for old mods, just give it a default palette
10011                 value = GET_ARG(1);
10012                 value2 = GET_ARG(2);
10013                 __realloc(mapflag, newchar->maps_loaded);
10014                 errorVal = load_colourmap(newchar, value, value2);
10015 
10016                 if(0 >= errorVal)
10017                 {
10018                     switch(errorVal)
10019                     {
10020                     case 0: // uhm wait, we just tested for !errorVal...
10021                         value2 = "Failed to create colourmap. Image Used Twice!";
10022                         break;
10023                     case -1: //should not happen now
10024                         value2 = "Failed to create colourmap. Color maps full error (color maps are unlimited by engine - check memory limits of console!";
10025                         break;
10026                     case -2:
10027                         value2 = "Failed to create colourmap. Failed to allocate memory!";
10028                         break;
10029                     case -3:
10030                         value2 = "Failed to create colourmap. Failed to load file 1";
10031                         break;
10032                     case -4:
10033                         value2 = "Failed to create colourmap. Failed to load file 2";
10034                         break;
10035                     }
10036                     printf("Warning: %s\n", value2);
10037                 }
10038                 else
10039                 {
10040                     if(pixelformat == PIXEL_x8 && newchar->palette == NULL)
10041                     {
10042                         newchar->palette = malloc(PAL_BYTES);
10043                         if(loadimagepalette(value, packfile, newchar->palette) == 0)
10044                         {
10045                             shutdownmessage = "Failed to load palette!";
10046                             goto lCleanup;
10047                         }
10048                     }
10049                     mapflag[newchar->maps_loaded - 1] = 1;
10050                 }
10051             }
10052             break;
10053             case CMD_MODEL_PALETTE:
10054 
10055                 if(newchar->palette == NULL)
10056                 {
10057 
10058                     // Command title for log. Details will be added blow accordingly.
10059                     // Forced character length is to line up with Alternatepal logs.
10060                     //printf("\t\t\tPalette: \t");
10061 
10062                     // Get argument.
10063                     value = GET_ARG(1);
10064 
10065                     // If "none" then set nopalette, meaning each
10066                     // frame retains its own color table as is.
10067                     // Otherwise we will set up the model level
10068                     // color table.
10069                     if(stricmp(value, "none") == 0)
10070                     {
10071                         nopalette = 1;
10072 
10073                         //printf("%s\n", "'None' option active. All sprites for this model will be loaded with independent color tables.");
10074                     }
10075                     else
10076                     {
10077                         // Set up the model level color table.
10078                         // We can load either directly from
10079                         // an .act file or read the color table
10080                         // from an image.
10081 
10082                         // Allocate space for the color table.
10083                         newchar->palette = malloc(PAL_BYTES);
10084 
10085                         if(load_palette(newchar->palette, value) == 0)
10086                         {
10087                             //printf("%s%s\n", "Failed to load color table from file: ", value);
10088                             goto lCleanup;
10089                         }
10090 
10091                         //printf("%s%s\n", "Loaded color selection 0: ", value);
10092                     }
10093                 }
10094 
10095                 break;
10096             case CMD_MODEL_ALTERNATEPAL:
10097 
10098                 // Command title for log. Details will be added blow accordingly.
10099                 //printf("\t"LOG_CMD_TITLE"%s", "Alternatepal", " - ");
10100 
10101                 __realloc(mapflag, newchar->maps_loaded);
10102                 __realloc(newchar->colourmap, newchar->maps_loaded);
10103                 value = GET_ARG(1);
10104 
10105                 newchar->colourmap[newchar->maps_loaded] = malloc(PAL_BYTES);
10106 
10107                 if(load_palette(newchar->colourmap[newchar->maps_loaded], value) == 0)
10108                 {
10109                     //printf("%s%s", "Failed to load color table from file: ", value);
10110                     goto lCleanup;
10111                 }
10112 
10113                 newchar->maps_loaded++;
10114 
10115                 //printf("Loaded color selection %i: %s", newchar->maps_loaded, value);
10116 
10117                 break;
10118             case CMD_MODEL_GLOBALMAP:
10119 
10120                 // Command title for log. Details will be added blow accordingly.
10121                 //printf("\t"LOG_CMD_TITLE"%s", "Globalmap", " - ");
10122 
10123                 // use global palette under 24bit mode, so some entity/panel/bg can still use palette feature, that saves some memory
10124                 newchar->globalmap = GET_INT_ARG(1);
10125 
10126                 //printf("%i: %s\n", newchar->globalmap, value);
10127 
10128                 break;
10129             case CMD_MODEL_ALPHA:
10130                 newchar->alpha = GET_INT_ARG(1);
10131                 break;
10132             case CMD_MODEL_REMOVE:
10133                 newchar->remove = GET_INT_ARG(1);
10134                 break;
10135             case CMD_MODEL_SCRIPT:
10136                 //load the update script
10137                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->update_script, "updateentityscript", filename, 1, 0);
10138                 break;
10139             case CMD_MODEL_THINKSCRIPT:
10140                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->think_script, "thinkscript", filename, 1, 0);
10141                 break;
10142             case CMD_MODEL_TAKEDAMAGESCRIPT:
10143                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->takedamage_script, "takedamagescript", filename, 1, 0);
10144                 break;
10145             case CMD_MODEL_ON_BIND_UPDATE_OTHER_TO_SELF_SCRIPT:
10146                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->on_bind_update_other_to_self_script, "on_bind_update_other_to_self_script", filename, 1, 0);
10147                 break;
10148             case CMD_MODEL_ON_BIND_UPDATE_SELF_TO_OTHER_SCRIPT:
10149                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->on_bind_update_self_to_other_script, "on_bind_update_self_to_other_script", filename, 1, 0);
10150                 break;
10151             case CMD_MODEL_ONFALLSCRIPT:
10152                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onfall_script, "onfallscript", filename, 1, 0);
10153                 break;
10154             case CMD_MODEL_ONPAINSCRIPT:
10155                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onpain_script, "onpainscript", filename, 1, 0);
10156                 break;
10157             case CMD_MODEL_INHOLESCRIPT:
10158                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->inhole_script, "inholescript", filename, 1, 0);
10159                 break;
10160             case CMD_MODEL_ONBLOCKSSCRIPT:
10161                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocks_script, "onblocksscript", filename, 1, 0);
10162                 break;
10163             case CMD_MODEL_ONBLOCKWSCRIPT:
10164                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockw_script, "onblockwscript", filename, 1, 0);
10165                 break;
10166             case CMD_MODEL_ONBLOCKPSCRIPT:
10167                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockp_script, "onblockpscript", filename, 1, 0);
10168                 break;
10169             case CMD_MODEL_ONBLOCKOSCRIPT:
10170                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocko_script, "onblockoscript", filename, 1, 0);
10171                 break;
10172             case CMD_MODEL_ONBLOCKZSCRIPT:
10173                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockz_script, "onblockzscript", filename, 1, 0);
10174                 break;
10175             case CMD_MODEL_ONBLOCKASCRIPT:
10176                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocka_script, "onblockascript", filename, 1, 0);
10177                 break;
10178             case CMD_MODEL_ONMOVEXSCRIPT:
10179                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovex_script, "onmovexscript", filename, 1, 0);
10180                 break;
10181             case CMD_MODEL_ONMOVEZSCRIPT:
10182                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovez_script, "onmovezscript", filename, 1, 0);
10183                 break;
10184             case CMD_MODEL_ONMOVEASCRIPT:
10185                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovea_script, "onmoveascript", filename, 1, 0);
10186                 break;
10187             case CMD_MODEL_ONDEATHSCRIPT:
10188                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondeath_script, "ondeathscript", filename, 1, 0);
10189                 break;
10190             case CMD_MODEL_ONENTITYCOLLISIONSCRIPT:
10191                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onentitycollision_script, "onentitycollisionscript", filename, 1, 0);
10192                 break;
10193             case CMD_MODEL_ONKILLSCRIPT:
10194                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onkill_script, "onkillscript", filename, 1, 0);
10195                 break;
10196             case CMD_MODEL_DIDBLOCKSCRIPT:
10197                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->didblock_script, "didblockscript", filename, 1, 0);
10198                 break;
10199             case CMD_MODEL_ONDOATTACKSCRIPT:
10200                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondoattack_script, "ondoattackscript", filename, 1, 0);
10201                 break;
10202             case CMD_MODEL_DIDHITSCRIPT:
10203                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->didhit_script, "didhitscript", filename, 1, 0);
10204                 break;
10205             case CMD_MODEL_ONSPAWNSCRIPT:
10206                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onspawn_script, "onspawnscript", filename, 1, 0);
10207                 break;
10208             case CMD_MODEL_ONMODELCOPYSCRIPT:
10209                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmodelcopy_script, "onmodelcopyscript", filename, 1, 0);
10210                 break;
10211             case CMD_MODEL_ONDRAWSCRIPT:
10212                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondraw_script, "ondrawscript", filename, 1, 0);
10213                 break;
10214             case CMD_MODEL_ANIMATIONSCRIPT:
10215                 //pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->animation_script, "animationscript", filename, 0, 0);
10216                 pos += lcmScriptCopyBuffer(&arglist, buf + pos, &animscriptbuf);
10217                 //dont compile, until at end of this function
10218                 break;
10219             case CMD_MODEL_KEYSCRIPT:
10220                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->key_script, "entitykeyscript", filename, 1, 0);
10221                 break;
10222             case CMD_MODEL_ANIM:
10223             {
10224                 value = GET_ARG(1);
10225                 frameset = 0;
10226                 framecount = 0;
10227                 // Create new animation
10228                 newanim = alloc_anim();
10229                 if(!newanim)
10230                 {
10231                     shutdownmessage = "Not enough memory for animations!";
10232                     goto lCleanup;
10233                 }
10234                 newanim->model_index = newchar->index;
10235                 // Reset vars
10236                 curframe = 0;
10237                 memset(&ebox, 0, sizeof(ebox));
10238                 memset(&bbox, 0, sizeof(bbox));
10239                 memset(&abox, 0, sizeof(abox));
10240                 memset(&offset, 0, sizeof(offset));
10241                 memset(shadow_coords, 0, sizeof(shadow_coords));
10242                 memset(shadow_xz, 0, sizeof(shadow_xz));
10243                 memset(platform, 0, sizeof(platform));
10244                 shadow_set                      = 0;
10245                 bbox_con                        = empty_body;
10246                 ebox_con                        = empty_entity_collision;
10247                 body_coords                     = empty_collision_coords;
10248                 attack                          = emptyattack;
10249 				attack.dropv					= default_model_dropv;
10250                 attack_coords                   = empty_collision_coords;
10251                 recursive                       = empty_recursive;
10252                 attack.hitsound                 = SAMPLE_BEAT;
10253                 attack.hitflash                 = -1;
10254                 attack.blockflash               = -1;
10255                 attack.blocksound               = -1;
10256                 drawmethod                      = plainmethod;
10257                 idle                            = 0;
10258                 move.base                       = -1;
10259                 move.axis.x                     = 0;
10260                 move.axis.y                     = 0;
10261                 move.axis.z                     = 0;
10262                 frameshadow                     = -1;
10263                 soundtoplay                     = -1;
10264 
10265                 if(!newanim->range.x.min)
10266                 {
10267                     newanim->range.x.min = -10;
10268                 }
10269                 newanim->range.x.max            = (int)newchar->jumpheight * 20;		// 30-12-2004 default range affected by jump height
10270                 newanim->range.z.min            = (int) - newchar->grabdistance / 3;	//zmin
10271                 newanim->range.z.max            = (int)newchar->grabdistance / 3;		//zmax
10272                 newanim->range.y.min            = 0;									//amin
10273 				newanim->range.y.max			= (int)newchar->jumpheight * 20;			// Same logic as X. Good for attacks, but not terrian. Author better remember to add jump ranges.
10274                 newanim->range.base.min         = 0;									// Base min.
10275 				newanim->range.base.max			= (int)newchar->jumpheight * 20;			// Just use same logic as range Y.
10276                 newanim->energycost             = NULL;
10277                 newanim->chargetime             = 2;			// Default for backwards compatibility
10278                 newanim->projectile.shootframe  = -1;
10279                 newanim->projectile.throwframe  = -1;
10280                 newanim->projectile.tossframe   = -1;			// this get 1 of weapons numshots shots in the animation that you want(normaly the last)by tails
10281                 newanim->flipframe              = -1;
10282                 newanim->attackone              = 0;
10283                 newanim->antigrav               = 0;
10284                 newanim->followup.animation     = 0;			// Default disabled
10285                 newanim->followup.condition     = FOLLOW_CONDITION_DISABLED;
10286                 newanim->unsummonframe          = -1;
10287                 newanim->landframe              = NULL;
10288                 newanim->jumpframe              = NULL;
10289                 newanim->dropframe              = NULL;
10290                 newanim->cancel                 = 0;  // OX. For cancelling anims into a freespecial. 0 by default , 3 when enabled. IMPORTANT!! Must stay as it is!
10291                 newanim->animhits               = 0; //OX counts hits on a per anim basis for cancels.
10292                 newanim->subentity              = newanim->projectile.bomb = newanim->projectile.knife =
10293                                                   newanim->projectile.star = newanim->projectile.flash = -1;
10294                 newanim->quakeframe.framestart  = 0;
10295                 newanim->sync                   = -1;
10296 
10297                 if((ani_id = translate_ani_id(value, newchar, newanim, &attack)) < 0)
10298                 {
10299                     shutdownmessage = "Invalid animation name!";
10300                     goto lCleanup;
10301                 }
10302 
10303                 newchar->animation[ani_id] = newanim;
10304             }
10305             break;
10306             case CMD_MODEL_LOOP:
10307                 if(!newanim)
10308                 {
10309                     shutdownmessage = "Can't set loop: no animation specified!";
10310                     goto lCleanup;
10311                 }
10312                 newanim->loop.mode      = GET_INT_ARG(1); //0 = Off, 1 = on.
10313                 newanim->loop.frame.min = GET_INT_ARG(2); //Loop to frame.
10314                 newanim->loop.frame.max = GET_INT_ARG(3); //Loop end frame.
10315                 break;
10316             case CMD_MODEL_ANIMHEIGHT:
10317                 newanim->size.y = GET_INT_ARG(1);
10318                 break;
10319             case CMD_MODEL_SYNC:
10320                 //if you want to remove default sync setting for idle or walk, use none
10321                 newanim->sync = translate_ani_id(GET_ARG(1), NULL, NULL, NULL);
10322                 break;
10323             case CMD_MODEL_DELAY:
10324                 delay = GET_INT_ARG(1);
10325                 break;
10326             case CMD_MODEL_OFFSET:
10327                 offset.x = GET_INT_ARG(1);
10328                 offset.y = GET_INT_ARG(2);
10329                 break;
10330             case CMD_MODEL_SHADOWCOORDS:
10331                 shadow_xz[0] = GET_INT_ARG(1);
10332                 shadow_xz[1] = GET_INT_ARG(2);
10333                 shadow_set = 1;
10334                 break;
10335             case CMD_MODEL_ENERGYCOST:
10336             case CMD_MODEL_MPCOST:
10337                 if(!newanim->energycost)
10338                 {
10339                     newanim->energycost    = malloc(sizeof(*newanim->energycost));
10340                     memset(newanim->energycost, 0, sizeof(*newanim->energycost));
10341                 }
10342 
10343                 newanim->energycost->cost    = GET_INT_ARG(1);
10344                 newanim->energycost->mponly  = GET_INT_ARG(2);
10345                 newanim->energycost->disable = GET_INT_ARG(3);
10346 
10347                 break;
10348             case CMD_MODEL_MPONLY:
10349                 if(!newanim->energycost)
10350                 {
10351                     newanim->energycost    = malloc(sizeof(*newanim->energycost));
10352                     memset(newanim->energycost, 0, sizeof(*newanim->energycost));
10353                 }
10354 
10355                 newanim->energycost->mponly = GET_INT_ARG(1);
10356                 break;
10357             case CMD_MODEL_CHARGETIME:
10358                 newanim->chargetime = GET_INT_ARG(1);
10359                 break;
10360             case CMD_MODEL_COLLISIONONE:
10361                 newanim->attackone = GET_INT_ARG(1);
10362                 break;
10363             case CMD_MODEL_COUNTERATTACK:
10364                 attack.counterattack = GET_INT_ARG(1);
10365                 break;
10366             case CMD_MODEL_THROWFRAME:
10367             case CMD_MODEL_PSHOTFRAME:
10368             case CMD_MODEL_PSHOTFRAMEW:
10369             case CMD_MODEL_PSHOTFRAMENO:
10370                 newanim->projectile.throwframe = GET_FRAME_ARG(1);
10371                 newanim->projectile.position.y = GET_INT_ARG(2);
10372                 if(!newanim->projectile.position.y)
10373                 {
10374                     newanim->projectile.position.y = 70;
10375                 }
10376                 else if(newanim->projectile.position.y == -1)
10377                 {
10378                     newanim->projectile.position.y = 0;
10379                 }
10380                 break;
10381             case CMD_MODEL_SHOOTFRAME:
10382                 newanim->projectile.shootframe = GET_FRAME_ARG(1);
10383                 newanim->projectile.position.y = GET_INT_ARG(2);
10384                 if(newanim->projectile.position.y == -1)
10385                 {
10386                     newanim->projectile.position.y = 0;
10387                 }
10388                 break;
10389             case CMD_MODEL_TOSSFRAME:
10390             case CMD_MODEL_PBOMBFRAME:
10391                 newanim->projectile.tossframe = GET_FRAME_ARG(1);
10392                 newanim->projectile.position.y = GET_INT_ARG(2);
10393                 if(newanim->projectile.position.y < 0)
10394                 {
10395                     newanim->projectile.position.y = -1;
10396                 }
10397                 break;
10398             case CMD_MODEL_CUSTKNIFE:
10399             case CMD_MODEL_CUSTPSHOT:
10400             case CMD_MODEL_CUSTPSHOTW:
10401                 newanim->projectile.knife = get_cached_model_index(GET_ARG(1));
10402                 break;
10403             case CMD_MODEL_CUSTPSHOTNO:
10404                 newanim->projectile.flash = get_cached_model_index(GET_ARG(1));
10405                 break;
10406             case CMD_MODEL_CUSTBOMB:
10407             case CMD_MODEL_CUSTPBOMB:
10408                 newanim->projectile.bomb = get_cached_model_index(GET_ARG(1));
10409                 break;
10410             case CMD_MODEL_CUSTSTAR:
10411                 newanim->projectile.star = get_cached_model_index(GET_ARG(1));
10412                 break;
10413 
10414                 // UT: merge dive and jumpframe, because they can't be used at the same time
10415             case CMD_MODEL_DIVE:	//antigrav kicks
10416                 newanim->antigrav = 1;
10417 
10418                 // Allocate jumpframe and use it to set movement.
10419                 newanim->jumpframe    = malloc(sizeof(*newanim->jumpframe));
10420                 memset(newanim->jumpframe, 0, sizeof(*newanim->jumpframe));
10421 
10422                 newanim->jumpframe->frame = 0;
10423                 newanim->jumpframe->velocity.x = GET_FLOAT_ARG(1);
10424                 newanim->jumpframe->velocity.y = -GET_FLOAT_ARG(2);
10425                 newanim->jumpframe->ent = -1;
10426                 break;
10427             case CMD_MODEL_DIVE1:
10428                 newanim->antigrav = 1;
10429 
10430                 // Allocate jumpframe and use it to set movement.
10431                 newanim->jumpframe    = malloc(sizeof(*newanim->jumpframe));
10432                 memset(newanim->jumpframe, 0, sizeof(*newanim->jumpframe));
10433 
10434                 newanim->jumpframe->frame = 0;
10435                 newanim->jumpframe->velocity.x = GET_FLOAT_ARG(1);
10436                 newanim->jumpframe->ent = -1;
10437                 break;
10438             case CMD_MODEL_DIVE2:
10439                 newanim->antigrav = 1;
10440 
10441                 // Allocate jumpframe and use it to set movement.
10442                 newanim->jumpframe    = malloc(sizeof(*newanim->jumpframe));
10443                 memset(newanim->jumpframe, 0, sizeof(*newanim->jumpframe));
10444 
10445                 newanim->jumpframe->frame = 0;
10446                 newanim->jumpframe->velocity.y = -GET_FLOAT_ARG(1);
10447                 newanim->jumpframe->ent = -1;
10448                 break;
10449             case CMD_MODEL_JUMPFRAME:
10450             {
10451                 // Allocate jumpframe.
10452                 newanim->jumpframe    = malloc(sizeof(*newanim->jumpframe));
10453                 memset(newanim->jumpframe, 0, sizeof(*newanim->jumpframe));
10454 
10455                 newanim->jumpframe->frame        = GET_FRAME_ARG(1);
10456                 newanim->jumpframe->velocity.y   = GET_FLOAT_ARG(2);
10457 
10458                 value = GET_ARG(3);
10459                 if(value[0])
10460                 {
10461                     newanim->jumpframe->velocity.x = GET_FLOAT_ARG(3);
10462                     newanim->jumpframe->velocity.z = GET_FLOAT_ARG(4);
10463                 }
10464                 else // k, only for backward compatibility :((((((((((((((((
10465                 {
10466                     if(newanim->jumpframe->velocity.y <= 0)
10467                     {
10468                         if(newchar->type == TYPE_PLAYER)
10469                         {
10470                             newanim->jumpframe->velocity.y = newchar->jumpheight / 2;
10471                             newanim->jumpframe->velocity.z = 0;
10472                             newanim->jumpframe->velocity.x = 2;
10473                         }
10474                         else
10475                         {
10476                             newanim->jumpframe->velocity.y = newchar->jumpheight;
10477                             newanim->jumpframe->velocity.z = newanim->jumpframe->velocity.x = 0;
10478                         }
10479                     }
10480                     else
10481                     {
10482                         if(newchar->type != TYPE_ENEMY && newchar->type != TYPE_NPC)
10483                         {
10484                             newanim->jumpframe->velocity.z = newanim->jumpframe->velocity.x = 0;
10485                         }
10486                         else
10487                         {
10488                             newanim->jumpframe->velocity.z = 0;
10489                             newanim->jumpframe->velocity.x = (float)1.3;
10490                         }
10491                     }
10492                 }
10493 
10494                 value = GET_ARG(5);
10495                 if(value[0])
10496                 {
10497                     newanim->jumpframe->ent = get_cached_model_index(value);
10498                 }
10499                 else
10500                 {
10501                     newanim->jumpframe->ent = -1;
10502                 }
10503             }
10504             break;
10505             case CMD_MODEL_BOUNCEFACTOR:
10506                 newanim->bounce = GET_FLOAT_ARG(1);
10507                 break;
10508             case CMD_MODEL_LANDFRAME:
10509                 newanim->landframe    = malloc(sizeof(*newanim->landframe));
10510                 memset(newanim->landframe, 0, sizeof(*newanim->landframe));
10511 
10512                 // Landing frame.
10513                 newanim->landframe->frame = GET_FRAME_ARG(1);
10514 
10515                 // Entity to spawn when land frame triggers.
10516                 value = GET_ARG(2);
10517                 if(value[0])
10518                 {
10519                     newanim->landframe->ent = get_cached_model_index(value);
10520                 }
10521                 else
10522                 {
10523                     newanim->landframe->ent = -1;
10524                 }
10525 
10526                 break;
10527             case CMD_MODEL_DROPFRAME:
10528                 newanim->dropframe    = malloc(sizeof(*newanim->dropframe));
10529                 memset(newanim->dropframe, 0, sizeof(*newanim->dropframe));
10530 
10531                 newanim->dropframe->frame = GET_FRAME_ARG(1);
10532                 break;
10533             case CMD_MODEL_CANCEL:
10534             {
10535                 int i, t;
10536                 int add_flag = 0;
10537                 alloc_specials(newchar);
10538                 newanim->cancel = 3;
10539                 newchar->special[newchar->specials_loaded].numkeys = 0;
10540                 for(i = 0, t = 4; i < MAX_SPECIAL_INPUTS - 6; i++, t++)
10541                 {
10542                     value = GET_ARG(t);
10543                     if(!value[0])
10544                     {
10545                         break;
10546                     }
10547                     if(stricmp(value, "u") == 0)
10548                     {
10549                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEUP;
10550                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEUP;
10551                         ++newchar->special[newchar->specials_loaded].numkeys;
10552                     }
10553                     else if(stricmp(value, "d") == 0)
10554                     {
10555                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEDOWN;
10556                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEDOWN;
10557                         ++newchar->special[newchar->specials_loaded].numkeys;
10558                     }
10559                     else if(stricmp(value, "f") == 0)
10560                     {
10561                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_FORWARD;
10562                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_FORWARD;
10563                         ++newchar->special[newchar->specials_loaded].numkeys;
10564                     }
10565                     else if(stricmp(value, "b") == 0)
10566                     {
10567                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_BACKWARD;
10568                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_BACKWARD;
10569                         ++newchar->special[newchar->specials_loaded].numkeys;
10570                     }
10571                     else if(stricmp(value, "a") == 0 || stricmp(value, "a1") == 0)
10572                     {
10573                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK;
10574                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK;
10575                         ++newchar->special[newchar->specials_loaded].numkeys;
10576                     }
10577                     else if(stricmp(value, "a2") == 0)
10578                     {
10579                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK2;
10580                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK2;
10581                         ++newchar->special[newchar->specials_loaded].numkeys;
10582                     }
10583                     else if(stricmp(value, "a3") == 0)
10584                     {
10585                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK3;
10586                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK3;
10587                         ++newchar->special[newchar->specials_loaded].numkeys;
10588                     }
10589                     else if(stricmp(value, "a4") == 0)
10590                     {
10591                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK4;
10592                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK4;
10593                         ++newchar->special[newchar->specials_loaded].numkeys;
10594                     }
10595                     else if(stricmp(value, "j") == 0)
10596                     {
10597                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_JUMP;
10598                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_JUMP;
10599                         ++newchar->special[newchar->specials_loaded].numkeys;
10600                     }
10601                     else if(stricmp(value, "s") == 0 || stricmp(value, "k") == 0)
10602                     {
10603                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_SPECIAL;
10604                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_SPECIAL;
10605                         ++newchar->special[newchar->specials_loaded].numkeys;
10606                     }
10607                     else if(starts_with_num(value, "freespecial"))
10608                     {
10609                         get_tail_number(tempInt, value, "freespecial");
10610                         newchar->special[newchar->specials_loaded].anim = animspecials[tempInt - 1];
10611                         newchar->special[newchar->specials_loaded].frame.min = GET_INT_ARG(1); // stores start frame
10612                         newchar->special[newchar->specials_loaded].frame.max = GET_INT_ARG(2); // stores end frame
10613                         newchar->special[newchar->specials_loaded].cancel = ani_id;                    // stores current anim
10614                         newchar->special[newchar->specials_loaded].hits = GET_INT_ARG(3);// stores hits
10615                     }
10616                     else if(stricmp(value, "+") == 0 && i > 1)
10617                     {
10618                         add_flag = 1;
10619                         i -= 2;
10620                         continue;
10621                     }
10622                     else if(stricmp(value, "->") == 0 && i > 0)
10623                     {
10624                         // just for better reading
10625                         --i;
10626                         continue;
10627                     }
10628                     else
10629                     {
10630                         shutdownmessage = "Invalid cancel command!";
10631                         goto lCleanup;
10632                     }
10633                     add_flag = 0;
10634                 }
10635                 newchar->special[newchar->specials_loaded].steps = i - 1; // max steps
10636                 newchar->specials_loaded++;
10637             }
10638             break;
10639             case CMD_MODEL_SOUND:
10640                 soundtoplay = sound_load_sample(GET_ARG(1), packfile, 1);
10641                 break;
10642             case CMD_MODEL_HITFX:
10643                 if(stricmp(GET_ARG(1), "none") == 0)
10644                 {
10645                     attack.hitsound = -1;
10646                 }
10647                 else
10648                 {
10649                     attack.hitsound = sound_load_sample(GET_ARG(1), packfile, 1);
10650                 }
10651                 break;
10652             case CMD_MODEL_HITFLASH:
10653                 value = GET_ARG(1);
10654                 if(stricmp(value, "none") == 0)
10655                 {
10656                     attack.hitflash = -1;
10657                 }
10658                 else
10659                 {
10660                     attack.hitflash = get_cached_model_index(value);
10661                 }
10662                 break;
10663             case CMD_MODEL_BLOCKFLASH:
10664                 value = GET_ARG(1);
10665                 if(stricmp(value, "none") == 0)
10666                 {
10667                     attack.blockflash = -1;
10668                 }
10669                 else
10670                 {
10671                     attack.blockflash = get_cached_model_index(value);
10672                 }
10673                 break;
10674             case CMD_MODEL_BLOCKFX:
10675                 attack.blocksound = sound_load_sample(GET_ARG(1), packfile, 1);
10676                 break;
10677             case CMD_MODEL_FASTATTACK:
10678                 if(GET_INT_ARG(1))
10679                 {
10680                     attack.next_hit_time = GAME_SPEED / 20;
10681                 }
10682                 break;
10683             case CMD_MODEL_IGNOREATTACKID:
10684                 if(GET_INT_ARG(1))
10685                 {
10686                     attack.ignore_attack_id = 1;
10687                 }
10688                 break;
10689             case CMD_MODEL_BBOX:
10690                 bbox.x = GET_INT_ARG(1);
10691                 bbox.y = GET_INT_ARG(2);
10692                 bbox.width = GET_INT_ARG(3);
10693                 bbox.height = GET_INT_ARG(4);
10694                 bbox.z1 = GET_INT_ARG(5);
10695                 bbox.z2 = GET_INT_ARG(6);
10696                 break;
10697             case CMD_MODEL_BBOX_INDEX:
10698                 // Nothing yet - for future support of multiple boxes.
10699                 break;
10700             case CMD_MODEL_BBOX_POSITION_X:
10701                 bbox.x = GET_INT_ARG(1);
10702                 break;
10703             case CMD_MODEL_BBOX_POSITION_Y:
10704                 bbox.y = GET_INT_ARG(1);
10705                 break;
10706             case CMD_MODEL_BBOX_SIZE_X:
10707                 bbox.width = GET_INT_ARG(1);
10708                 break;
10709             case CMD_MODEL_BBOX_SIZE_Y:
10710                 bbox.height = GET_INT_ARG(1);
10711                 break;
10712             case CMD_MODEL_BBOX_SIZE_Z_1:
10713                 bbox.z1 = GET_INT_ARG(1);
10714                 break;
10715             case CMD_MODEL_BBOX_SIZE_Z_2:
10716                 bbox.z2 = GET_INT_ARG(1);
10717                 break;
10718             case CMD_MODEL_BBOXZ:
10719                 bbox.z1 = GET_INT_ARG(1);
10720                 bbox.z2 = GET_INT_ARG(2);
10721                 break;
10722             case CMD_MODEL_EBOX:
10723                 ebox.x = GET_INT_ARG(1);
10724                 ebox.y = GET_INT_ARG(2);
10725                 ebox.width = GET_INT_ARG(3);
10726                 ebox.height = GET_INT_ARG(4);
10727                 ebox.z1 = GET_INT_ARG(5);
10728                 ebox.z2 = GET_INT_ARG(6);
10729                 break;
10730             case CMD_MODEL_EBOX_INDEX:
10731                 // Nothing yet - for future support of multiple boxes.
10732                 break;
10733             case CMD_MODEL_EBOX_POSITION_X:
10734                 ebox.x = GET_INT_ARG(1);
10735                 break;
10736             case CMD_MODEL_EBOX_POSITION_Y:
10737                 ebox.y = GET_INT_ARG(1);
10738                 break;
10739             case CMD_MODEL_EBOX_SIZE_X:
10740                 ebox.width = GET_INT_ARG(1);
10741                 break;
10742             case CMD_MODEL_EBOX_SIZE_Y:
10743                 ebox.height = GET_INT_ARG(1);
10744                 break;
10745             case CMD_MODEL_EBOX_SIZE_Z_1:
10746                 ebox.z1 = GET_INT_ARG(1);
10747                 break;
10748             case CMD_MODEL_EBOX_SIZE_Z_2:
10749                 ebox.z2 = GET_INT_ARG(1);
10750                 break;
10751             case CMD_MODEL_EBOXZ:
10752                 ebox.z1 = GET_INT_ARG(1);
10753                 ebox.z2 = GET_INT_ARG(2);
10754                 break;
10755             case CMD_MODEL_PLATFORM:
10756                 newchar->hasPlatforms = 1;
10757                 //for(i=0;(GET_ARG(i+1)[0]; i++);
10758                 for(i = 0; i < arglist.count && arglist.args[i] && arglist.args[i][0]; i++);
10759                 if(i < 8)
10760                 {
10761                     for(i = 0; i < 6; i++)
10762                     {
10763                         platform[i + 2] = GET_FLOAT_ARG(i + 1);
10764                     }
10765                     platform[PLATFORM_X] = PLATFORM_DEFAULT_X;
10766                 }
10767                 else for(i = 0; i < 8; i++)
10768                     {
10769                         platform[i] = GET_FLOAT_ARG(i + 1);
10770                     }
10771                 break;
10772             case CMD_MODEL_DRAWMETHOD:
10773                 value = GET_ARG(1);
10774                 if(isNumeric(value))
10775                 {
10776                     // special effects
10777                     drawmethod.scalex = GET_INT_ARG(1);
10778                     drawmethod.scaley = GET_INT_ARG(2);
10779                     drawmethod.flipx = GET_INT_ARG(3);
10780                     drawmethod.flipy = GET_INT_ARG(4);
10781                     drawmethod.shiftx = GET_INT_ARG(5);
10782                     drawmethod.alpha = GET_INT_ARG(6);
10783                     drawmethod.remap = GET_INT_ARG(7);
10784                     drawmethod.fillcolor = parsecolor(GET_ARG(8));
10785                     drawmethod.rotate = GET_INT_ARG(9);
10786                     drawmethod.fliprotate = GET_INT_ARG(10);
10787                 }
10788                 else if (0 == stricmp(value, "scale"))
10789                 {
10790                     drawmethod.scalex = GET_FLOAT_ARG(2) * 256;
10791                     drawmethod.scaley = arglist.count > 3 ? GET_FLOAT_ARG(3) * 256 : drawmethod.scalex;
10792                 }
10793                 else if (0 == stricmp(value, "scalex"))
10794                 {
10795                     drawmethod.scalex = GET_FLOAT_ARG(2) * 256;
10796                 }
10797                 else if (0 == stricmp(value, "scaley"))
10798                 {
10799                     drawmethod.scaley = GET_FLOAT_ARG(2) * 256;
10800                 }
10801                 else if (0 == stricmp(value, "xrepeat"))
10802                 {
10803                     drawmethod.xrepeat = GET_INT_ARG(2);
10804                 }
10805                 else if (0 == stricmp(value, "yrepeat"))
10806                 {
10807                     drawmethod.yrepeat = GET_INT_ARG(2);
10808                 }
10809                 else if (0 == stricmp(value, "xspan"))
10810                 {
10811                     drawmethod.xspan = GET_INT_ARG(2);
10812                 }
10813                 else if (0 == stricmp(value, "yspan"))
10814                 {
10815                     drawmethod.yspan = GET_INT_ARG(2);
10816                 }
10817                 else if (0 == stricmp(value, "flipx"))
10818                 {
10819                     drawmethod.flipx = GET_INT_ARG(2);
10820                 }
10821                 else if (0 == stricmp(value, "flipy"))
10822                 {
10823                     drawmethod.flipy = GET_INT_ARG(2);
10824                 }
10825                 else if (0 == stricmp(value, "shiftx"))
10826                 {
10827                     drawmethod.shiftx = GET_FLOAT_ARG(2) * 256;
10828                 }
10829                 else if (0 == stricmp(value, "rotate"))
10830                 {
10831                     drawmethod.rotate = GET_INT_ARG(2);
10832                 }
10833                 else if (0 == stricmp(value, "fliprotate"))
10834                 {
10835                     drawmethod.fliprotate = GET_INT_ARG(2);
10836                 }
10837                 else if (0 == stricmp(value, "fillcolor"))
10838                 {
10839                     drawmethod.fliprotate = parsecolor(GET_ARG(2));
10840                 }
10841                 else if (0 == stricmp(value, "remap"))
10842                 {
10843                     drawmethod.remap = GET_INT_ARG(2);
10844                 }
10845                 else if (0 == stricmp(value, "channel"))
10846                 {
10847                     drawmethod.channelr = GET_FLOAT_ARG(2) * 255;
10848                     drawmethod.channelg = arglist.count > 3 ? GET_FLOAT_ARG(3) * 255 : drawmethod.channelr;
10849                     drawmethod.channelb = arglist.count > 4 ? GET_FLOAT_ARG(4) * 255 : drawmethod.channelr;
10850                 }
10851                 else if (0 == stricmp(value, "channelr"))
10852                 {
10853                     drawmethod.channelr = GET_FLOAT_ARG(2) * 255;
10854                 }
10855                 else if (0 == stricmp(value, "channelg"))
10856                 {
10857                     drawmethod.channelg = GET_FLOAT_ARG(2) * 255;
10858                 }
10859                 else if (0 == stricmp(value, "channelb"))
10860                 {
10861                     drawmethod.channelb = GET_FLOAT_ARG(2) * 255;
10862                 }
10863                 else if (0 == stricmp(value, "tintmode"))
10864                 {
10865                     drawmethod.tintmode = GET_INT_ARG(2);
10866                 }
10867                 else if (0 == stricmp(value, "tintcolor"))
10868                 {
10869                     drawmethod.tintcolor = parsecolor(GET_ARG(2));
10870                 }
10871                 else if (0 == stricmp(value, "alpha"))
10872                 {
10873                     drawmethod.alpha = GET_INT_ARG(2);
10874                 }
10875                 else if (0 == stricmp(value, "clip"))
10876                 {
10877                     drawmethod.clipx = GET_INT_ARG(2);
10878                     drawmethod.clipy = GET_INT_ARG(3);
10879                     drawmethod.clipw = GET_INT_ARG(4);
10880                     drawmethod.cliph = GET_INT_ARG(5);
10881                 }
10882                 if(drawmethod.scalex < 0)
10883                 {
10884                     drawmethod.scalex = -drawmethod.scalex;
10885                     drawmethod.flipx = !drawmethod.flipx;
10886                 }
10887                 if(drawmethod.scaley < 0)
10888                 {
10889                     drawmethod.scaley = -drawmethod.scaley;
10890                     drawmethod.flipy = !drawmethod.flipy;
10891                 }
10892                 if(drawmethod.rotate)
10893                 {
10894                     drawmethod.rotate = ((int)drawmethod.rotate % 360 + 360) % 360;
10895                 }
10896                 if(!blendfx_is_set)
10897                 {
10898                     if(drawmethod.alpha > 0 && drawmethod.alpha <= MAX_BLENDINGS)
10899                     {
10900                         blendfx[drawmethod.alpha - 1] = 1;
10901                     }
10902                 }
10903                 drawmethod.flag = 1;
10904                 break;
10905             case CMD_MODEL_NODRAWMETHOD:
10906                 //disable special effects
10907                 drawmethod.flag = 0;
10908                 break;
10909 
10910             // 2016-10-11
10911             // Caskey, Damon
10912             // Broken down attack commands.
10913             case CMD_MODEL_COLLISION_BLOCK_COST:
10914                 attack.guardcost = GET_INT_ARG(1);
10915                 break;
10916             case CMD_MODEL_COLLISION_BLOCK_PENETRATE:
10917                 attack.no_block = GET_INT_ARG(1);
10918                 break;
10919             case CMD_MODEL_COLLISION_COUNTER:
10920                 attack.counterattack = GET_INT_ARG(1);
10921                 break;
10922             case CMD_MODEL_COLLISION_DAMAGE_FORCE:
10923                 attack.attack_force = GET_INT_ARG(1);
10924                 break;
10925             case CMD_MODEL_COLLISION_DAMAGE_LAND_FORCE:
10926                 attack.damage_on_landing.attack_force = GET_INT_ARG(1);
10927                 break;
10928             case CMD_MODEL_COLLISION_DAMAGE_LAND_MODE:
10929                 attack.blast = GET_INT_ARG(1);
10930                 break;
10931             case CMD_MODEL_COLLISION_DAMAGE_LETHAL_DISABLE:
10932                 attack.no_kill = GET_INT_ARG(1);
10933                 break;
10934             case CMD_MODEL_COLLISION_DAMAGE_STEAL:
10935                 attack.steal = GET_INT_ARG(1);
10936                 break;
10937             case CMD_MODEL_COLLISION_DAMAGE_TYPE:
10938 
10939 				value = GET_ARG(1);
10940 
10941 				if (stricmp(value, "burn") == 0)
10942 				{
10943 					attack.attack_type = ATK_BURN;
10944 				}
10945 				else if(stricmp(value, "freeze") == 0)
10946 				{
10947 					attack.attack_type = ATK_FREEZE;
10948 				}
10949 				else if (stricmp(value, "shock") == 0)
10950 				{
10951 					attack.attack_type = ATK_SHOCK;
10952 				}
10953 				else if (stricmp(value, "steal") == 0)
10954 				{
10955 					attack.attack_type = ATK_STEAL;
10956 				}
10957 				else
10958 				{
10959 					attack.attack_type = GET_INT_ARG(1);
10960 				}
10961 
10962                 break;
10963 
10964             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_FORCE:
10965 				recursive.force = GET_INT_ARG(1);
10966                 break;
10967             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_INDEX:
10968 				recursive.index = GET_INT_ARG(1);
10969                 break;
10970             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_MODE:
10971 				recursive.mode = GET_INT_ARG(1);
10972                 break;
10973 			case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TAG:
10974 				recursive.tag = GET_INT_ARG(1);
10975 				break;
10976             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TIME_RATE:
10977 				recursive.rate = GET_INT_ARG(1);
10978                 break;
10979             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TIME_EXPIRE:
10980 				recursive.time = GET_INT_ARG(1);
10981                 break;
10982             case CMD_MODEL_COLLISION_REACTION_FALL_FORCE:
10983                 attack.attack_drop = GET_INT_ARG(1);
10984                 break;
10985             case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_X:
10986                 attack.dropv.x = GET_FLOAT_ARG(1);
10987                 break;
10988             case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_Y:
10989                 attack.dropv.y = GET_FLOAT_ARG(1);
10990                 break;
10991             case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_Z:
10992                 attack.dropv.z = GET_FLOAT_ARG(1);
10993                 break;
10994             case CMD_MODEL_COLLISION_EFFECT_BLOCK_FLASH:
10995 
10996                 value = GET_ARG(1);
10997 
10998                 if(stricmp(value, "none") == 0 || value == 0)
10999                 {
11000                     attack.blockflash = -1;
11001                 }
11002                 else
11003                 {
11004 					attack.blockflash = get_cached_model_index(value);
11005                 }
11006                 break;
11007 
11008             case CMD_MODEL_COLLISION_EFFECT_BLOCK_SOUND:
11009 
11010                 value = GET_ARG(1);
11011 
11012                 if(stricmp(value, "none") == 0)
11013                 {
11014                     attack.blocksound = -1;
11015                 }
11016                 else
11017                 {
11018                     attack.blocksound = sound_load_sample(value, packfile, 1);
11019                 }
11020                 break;
11021 
11022             case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH:
11023 
11024                 value = GET_ARG(1);
11025 
11026                 if(stricmp(value, "none") == 0 || value == 0)
11027                 {
11028                     attack.hitflash = -1;
11029                 }
11030                 else
11031                 {
11032                     attack.hitflash = get_cached_model_index(value);
11033                 }
11034                 break;
11035 
11036             case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH_DISABLE:
11037                 attack.no_flash = GET_INT_ARG(1);
11038                 break;
11039 
11040             case CMD_MODEL_COLLISION_EFFECT_HIT_SOUND:
11041 
11042                 value = GET_ARG(1);
11043 
11044                 if(stricmp(value, "none") == 0)
11045                 {
11046                     attack.hitsound = -1;
11047                 }
11048                 else
11049                 {
11050                     attack.hitsound = sound_load_sample(value, packfile, 1);
11051                 }
11052                 break;
11053             case CMD_MODEL_COLLISION_GROUND:
11054                 attack.otg = GET_INT_ARG(1);
11055                 break;
11056             case CMD_MODEL_COLLISION_MAP_INDEX:
11057                 attack.forcemap = GET_INT_ARG(1);
11058                 break;
11059             case CMD_MODEL_COLLISION_MAP_TIME:
11060                 attack.maptime = GET_INT_ARG(1);
11061                 break;
11062             case CMD_MODEL_COLLISION_POSITION_X:
11063                 abox.x = GET_INT_ARG(1);
11064                 break;
11065             case CMD_MODEL_COLLISION_POSITION_Y:
11066                 abox.y = GET_INT_ARG(1);
11067                 break;
11068             case CMD_MODEL_COLLISION_REACTION_FREEZE_MODE:
11069                 attack.freeze = GET_INT_ARG(1);
11070                 break;
11071             case CMD_MODEL_COLLISION_REACTION_FREEZE_TIME:
11072                 attack.freezetime = GET_INT_ARG(1);
11073                 break;
11074             case CMD_MODEL_COLLISION_REACTION_INVINCIBLE_TIME:
11075                 attack.next_hit_time = GET_INT_ARG(1);
11076                 break;
11077             case CMD_MODEL_COLLISION_REACTION_REPOSITION_DISTANCE:
11078                 attack.grab_distance = GET_INT_ARG(1);
11079                 break;
11080             case CMD_MODEL_COLLISION_REACTION_REPOSITION_MODE:
11081                 attack.grab = GET_INT_ARG(1);
11082                 break;
11083             case CMD_MODEL_COLLISION_REACTION_PAIN_SKIP:
11084                 attack.no_pain = GET_INT_ARG(1);
11085                 break;
11086             case CMD_MODEL_COLLISION_REACTION_PAUSE_TIME:
11087                 attack.pause_add = GET_INT_ARG(1);
11088                 break;
11089             case CMD_MODEL_COLLISION_SEAL_COST:
11090                 attack.seal = GET_INT_ARG(1);
11091                 break;
11092             case CMD_MODEL_COLLISION_SEAL_TIME:
11093                 attack.sealtime = GET_INT_ARG(1);
11094                 break;
11095             case CMD_MODEL_COLLISION_SIZE_X:
11096                 abox.width = GET_INT_ARG(1);
11097                 break;
11098             case CMD_MODEL_COLLISION_SIZE_Y:
11099                 abox.height = GET_INT_ARG(1);
11100                 break;
11101             case CMD_MODEL_COLLISION_SIZE_Z_1:
11102                 attack_coords.z1 = GET_INT_ARG(1);
11103                 break;
11104             case CMD_MODEL_COLLISION_SIZE_Z_2:
11105                 attack_coords.z2 = GET_INT_ARG(1);
11106                 break;
11107             case CMD_MODEL_COLLISION_STAYDOWN_RISE:
11108                 attack.staydown.rise = GET_INT_ARG(1);
11109                 break;
11110             case CMD_MODEL_COLLISION_STAYDOWN_RISEATTACK:
11111                 attack.staydown.riseattack = GET_INT_ARG(1);
11112                 break;
11113             case CMD_MODEL_COLLISION_TAG:
11114                 attack.tag = GET_INT_ARG(1);
11115                 break;
11116             case CMD_MODEL_COLLISION:
11117             case CMD_MODEL_COLLISION1:
11118             case CMD_MODEL_COLLISION2:
11119             case CMD_MODEL_COLLISION3:
11120             case CMD_MODEL_COLLISION4:
11121             case CMD_MODEL_COLLISION5:
11122             case CMD_MODEL_COLLISION6:
11123             case CMD_MODEL_COLLISION7:
11124             case CMD_MODEL_COLLISION8:
11125             case CMD_MODEL_COLLISION9:
11126             case CMD_MODEL_COLLISION10:
11127             case CMD_MODEL_SHOCK:
11128             case CMD_MODEL_BURN:
11129             case CMD_MODEL_STEAL:
11130             case CMD_MODEL_FREEZE:
11131             case CMD_MODEL_ITEMBOX:
11132             case CMD_MODEL_LOSE:
11133             case CMD_MODEL_COLLISION_ETC:
11134                 abox.x = GET_INT_ARG(1);
11135                 abox.y = GET_INT_ARG(2);
11136                 abox.width = GET_INT_ARG(3);
11137                 abox.height = GET_INT_ARG(4);
11138                 attack.attack_force = GET_INT_ARG(5);
11139 
11140                 attack.attack_drop = GET_INT_ARG(6);
11141 
11142                 attack.no_block = GET_INT_ARG(7);
11143                 attack.no_flash = GET_INT_ARG(8);
11144                 attack.pause_add = GET_INT_ARG(9);
11145                 attack_coords.z1 = GET_INT_ARG(10); // depth or z
11146 
11147                 switch(cmd)
11148                 {
11149                 case CMD_MODEL_COLLISION:
11150                 case CMD_MODEL_COLLISION1:
11151                     attack.attack_type = ATK_NORMAL;
11152                     break;
11153                 case CMD_MODEL_COLLISION2:
11154                     attack.attack_type  = ATK_NORMAL2;
11155                     break;
11156                 case CMD_MODEL_COLLISION3:
11157                     attack.attack_type  = ATK_NORMAL3;
11158                     break;
11159                 case CMD_MODEL_COLLISION4:
11160                     attack.attack_type  = ATK_NORMAL4;
11161                     break;
11162                 case CMD_MODEL_COLLISION5:
11163                     attack.attack_type  = ATK_NORMAL5;
11164                     break;
11165                 case CMD_MODEL_COLLISION6:
11166                     attack.attack_type  = ATK_NORMAL6;
11167                     break;
11168                 case CMD_MODEL_COLLISION7:
11169                     attack.attack_type  = ATK_NORMAL7;
11170                     break;
11171                 case CMD_MODEL_COLLISION8:
11172                     attack.attack_type  = ATK_NORMAL8;
11173                     break;
11174                 case CMD_MODEL_COLLISION9:
11175                     attack.attack_type  = ATK_NORMAL9;
11176                     break;
11177                 case CMD_MODEL_COLLISION10:
11178                     attack.attack_type  = ATK_NORMAL10;
11179                     break;
11180                 case CMD_MODEL_SHOCK:
11181                     attack.attack_type  = ATK_SHOCK;
11182                     break;
11183                 case CMD_MODEL_BURN:
11184                     attack.attack_type  = ATK_BURN;
11185                     break;
11186                 case CMD_MODEL_STEAL:
11187                     attack.steal = 1;
11188                     attack.attack_type  = ATK_STEAL;
11189                     break;
11190                 case CMD_MODEL_FREEZE:
11191                     attack.attack_type  = ATK_FREEZE;
11192                     attack.freeze = 1;
11193                     attack.freezetime = GET_FLOAT_ARG(6) * GAME_SPEED;
11194                     attack.forcemap = -1;
11195                     attack.attack_drop = 0;
11196                     break;
11197                 case CMD_MODEL_ITEMBOX:
11198                     attack.attack_type  = ATK_ITEM;
11199                     break;
11200                 case CMD_MODEL_LOSE:
11201                     attack.attack_type  = ATK_LOSE;
11202                     attack.attack_drop = 0;
11203                     break;
11204                 default:
11205                     tempInt = atoi(command + 6);
11206                     if(tempInt < MAX_ATKS - STA_ATKS + 1)
11207                     {
11208                         tempInt = MAX_ATKS - STA_ATKS + 1;
11209                     }
11210                     attack.attack_type = tempInt + STA_ATKS - 1;
11211                 }
11212                 break;
11213             case CMD_MODEL_HITWALLTYPE:
11214                 value = GET_ARG(1);
11215                 newchar->hitwalltype = atoi(value);
11216                 break;
11217             case CMD_MODEL_COLLISIONZ:
11218             case CMD_MODEL_HITZ:
11219                 attack_coords.z1 = GET_INT_ARG(1);
11220                 attack_coords.z2 = GET_INT_ARG(2);
11221                 break;
11222             case CMD_MODEL_BLAST:
11223                 abox.x = GET_INT_ARG(1);
11224                 abox.y = GET_INT_ARG(2);
11225                 abox.width = GET_INT_ARG(3);
11226                 abox.height = GET_INT_ARG(4);
11227                 attack.dropv.y = default_model_dropv.y;
11228                 attack.dropv.x = default_model_dropv.x * 2.083f;
11229                 attack.dropv.z = 0;
11230                 attack.attack_force = GET_INT_ARG(5);
11231                 attack.no_block = GET_INT_ARG(6);
11232                 attack.no_flash = GET_INT_ARG(7);
11233                 attack.pause_add = GET_INT_ARG(8);
11234                 attack.attack_drop = 1;
11235                 attack.attack_type = ATK_BLAST;
11236                 attack_coords.z1 = GET_INT_ARG(9); // depth or z
11237                 attack.blast = 1;
11238                 break;
11239             case CMD_MODEL_DROPV:
11240                 // drop velocity add if the target is knocked down
11241                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11242                 pattack->dropv.y = GET_FLOAT_ARG(1); // height add
11243                 pattack->dropv.x = GET_FLOAT_ARG(2); // xdir add
11244                 pattack->dropv.z = GET_FLOAT_ARG(3); // zdir add
11245                 break;
11246             case CMD_MODEL_OTG:
11247                 // Over The Ground hit.
11248                 attack.otg = GET_INT_ARG(1);
11249                 break;
11250             case CMD_MODEL_JUGGLECOST:
11251                 // if cost >= opponents jugglepoints , we can juggle
11252                 attack.jugglecost = GET_INT_ARG(1);
11253                 break;
11254             case CMD_MODEL_GUARDCOST:
11255                 // if cost >= opponents guardpoints , opponent will play guardcrush anim
11256                 attack.guardcost = GET_INT_ARG(1);
11257                 break;
11258             case CMD_MODEL_STUN:
11259                 //Like Freeze, but no auto remap.
11260                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11261                 pattack->freeze = 1;
11262                 pattack->freezetime = GET_FLOAT_ARG(1) * GAME_SPEED;
11263                 pattack->attack_drop = 0;
11264                 break;
11265             case CMD_MODEL_GRABIN:
11266                 // fake grab distanse efffect, not link
11267                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11268                 pattack->grab =  GET_INT_ARG(1);
11269                 pattack->grab_distance = GET_FLOAT_ARG(2);
11270                 break;
11271             case CMD_MODEL_NOREFLECT:
11272                 // only cost target's hp, don't knock down or cause pain, unless the target is killed
11273                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11274                 pattack->no_pain = GET_INT_ARG(1);
11275                 break;
11276             case CMD_MODEL_NOKILL:
11277                 // don't kill the target, leave 1 hp
11278                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11279                 pattack->no_kill = GET_INT_ARG(1);
11280                 break;
11281             case CMD_MODEL_FORCEDIRECTION:
11282                 // the attack direction
11283                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11284                 pattack->force_direction = GET_INT_ARG(1);
11285                 break;
11286             case CMD_MODEL_DAMAGEONLANDING:
11287                 // fake throw damage on landing
11288                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11289                 pattack->damage_on_landing.attack_force = GET_INT_ARG(1);
11290                 pattack->blast = GET_INT_ARG(2);
11291                 pattack->damage_on_landing.attack_type = translate_attack_type(GET_ARG(3));
11292                 break;
11293             case CMD_MODEL_SEAL:
11294                 // Disable special moves for specified time.
11295                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11296                 pattack->sealtime = GET_INT_ARG(1) * GAME_SPEED;
11297                 pattack->seal = GET_INT_ARG(2);
11298                 break;
11299             case CMD_MODEL_STAYDOWN:
11300                 // Disable special moves for specified time.
11301                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11302                 pattack->staydown.rise          = GET_INT_ARG(1); //Risetime modifier.
11303                 pattack->staydown.riseattack    = GET_INT_ARG(2); //Riseattack time addition and toggle.
11304                 break;
11305             case CMD_MODEL_DOT:
11306 
11307                 recursive.index  = GET_INT_ARG(1);  //Index.
11308                 recursive.time   = GET_INT_ARG(2);  //Time to expiration.
11309                 recursive.mode	 = GET_INT_ARG(3);  //Mode, see damage_recursive.
11310                 recursive.force  = GET_INT_ARG(4);  //Amount per tick.
11311                 recursive.rate   = GET_INT_ARG(5);  //Tick delay.
11312 
11313                 break;
11314 
11315             case CMD_MODEL_FORCEMAP:
11316                 // force color map change for specified time
11317                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
11318                 pattack->forcemap = GET_INT_ARG(1);
11319                 pattack->maptime = GET_FLOAT_ARG(2) * GAME_SPEED;
11320                 break;
11321             case CMD_MODEL_IDLE:
11322                 idle = GET_INT_ARG(1);
11323                 break;
11324             case CMD_MODEL_SETA:
11325                 move.base = GET_INT_ARG(1);
11326                 break;
11327             case CMD_MODEL_MOVE:
11328                 move.axis.x = GET_INT_ARG(1);
11329                 break;
11330             case CMD_MODEL_MOVEA:
11331                 move.axis.y = GET_INT_ARG(1);
11332                 break;
11333             case CMD_MODEL_MOVEZ:
11334                 move.axis.z = GET_INT_ARG(1);
11335                 break;
11336             case CMD_MODEL_FSHADOW:
11337                 frameshadow = GET_INT_ARG(1);
11338                 break;
11339             case CMD_MODEL_RANGE:
11340                 if(!newanim)
11341                 {
11342                     shutdownmessage = "Cannot set range: no animation!";
11343                     goto lCleanup;
11344                 }
11345                 newanim->range.x.min = GET_INT_ARG(1);
11346                 newanim->range.x.max = GET_INT_ARG(2);
11347                 if(newanim->range.x.min == newanim->range.x.max)
11348                 {
11349                     newanim->range.x.min--;
11350                 }
11351                 break;
11352             case CMD_MODEL_RANGEZ:
11353                 if(!newanim)
11354                 {
11355                     shutdownmessage = "Cannot set rangez: no animation!";
11356                     goto lCleanup;
11357                 }
11358                 newanim->range.z.min = GET_INT_ARG(1);
11359                 newanim->range.z.max = GET_INT_ARG(2);
11360                 break;
11361             case CMD_MODEL_RANGEA:
11362                 if(!newanim)
11363                 {
11364                     shutdownmessage = "Cannot set rangea: no animation!";
11365                     goto lCleanup;
11366                 }
11367                 newanim->range.y.min = GET_INT_ARG(1);
11368                 newanim->range.y.max = GET_INT_ARG(2);
11369                 break;
11370             case CMD_MODEL_RANGEB:
11371                 if(!newanim)
11372                 {
11373                     shutdownmessage = "Cannot set rangeb: no animation!";
11374                     goto lCleanup;
11375                 }
11376                 newanim->range.base.min = GET_INT_ARG(1);
11377                 newanim->range.base.max = GET_INT_ARG(2);
11378                 break;
11379             case CMD_MODEL_PATHFINDSTEP:
11380                 newchar->pathfindstep = GET_FLOAT_ARG(1);
11381                 break;
11382             case CMD_MODEL_FRAME:
11383             {
11384                 // Command title for log. Details will be added blow accordingly.
11385                 //printf("\t\t\tFrame: ");
11386 
11387                 if(!newanim)
11388                 {
11389                     shutdownmessage = "Cannot add frame: animation not specified!";
11390                     goto lCleanup;
11391                 }
11392                 peek = 0;
11393                 if(frameset && framecount >= 0)
11394                 {
11395                     framecount = -framecount;
11396                 }
11397                 while(!frameset)
11398                 {
11399                     value3 = findarg(buf + pos + peek, 0);
11400                     if(stricmp(value3, "frame") == 0)
11401                     {
11402                         framecount++;
11403                     }
11404                     if((stricmp(value3, "anim") == 0) || (pos + peek >= size))
11405                     {
11406                         frameset = 1;
11407                     }
11408                     // Go to next line
11409                     while(buf[pos + peek] && buf[pos + peek] != '\n' && buf[pos + peek] != '\r')
11410                     {
11411                         ++peek;
11412                     }
11413                     while(buf[pos + peek] == '\n' || buf[pos + peek] == '\r')
11414                     {
11415                         ++peek;
11416                     }
11417                 }
11418                 value = GET_ARG(1);
11419 
11420                 // Log info.
11421                 //printf("%d", ++framenum);
11422                 //printf("\tSprite Path: %s\n", value);
11423 
11424                 index = stricmp(value, "none") == 0 ? -1 : loadsprite(value, offset.x, offset.y, nopalette ? PIXEL_x8 : PIXEL_8); //don't use palette for the sprite since it will one palette from the entity's remap list in 24bit mode
11425                 if(index >= 0)
11426                 {
11427                     // If the model does not have a designated palette
11428                     // yet and the author did not specify palette none
11429                     // or global palette, then we will use this frame's
11430                     // sprite to load a color table. Effectively the first
11431                     // frame of a model becomes its palette base.
11432                     if(pixelformat == PIXEL_x8 && !nopalette)
11433                     {
11434                         // No master color table assigned yet?
11435                         if(newchar->palette == NULL)
11436                         {
11437                             //printf("\t\t\tAuto Palette - 'Palette' not defined. Attempting to load color table from this frame: ");
11438 
11439                             // Allocate memory for color table.
11440                             newchar->palette = malloc(PAL_BYTES);
11441                             //
11442                             if(loadimagepalette(value, packfile, newchar->palette) == 0)
11443                             {
11444                                 //printf("\t\t\t%s%s\n", "Failed to load color table from image: ", value);
11445                                 goto lCleanup;
11446                             }
11447                         }
11448 
11449                         //printf("\t\t\t%s\n", "Success. Loaded color selection 0 from frame.");
11450 
11451                         // Assign the color table to sprite.
11452                         if(!nopalette)
11453                         {
11454                             sprite_map[index].node->sprite->palette = newchar->palette;
11455                             sprite_map[index].node->sprite->pixelformat = pixelformat;
11456                         }
11457                     }
11458 
11459                     if(maskindex >= 0)
11460                     {
11461                         sprite_map[index].node->sprite->mask = sprite_map[maskindex].node->sprite;
11462                         maskindex = -1;
11463                     }
11464                 }
11465                 // Adjust coords: add offsets and change size to coords
11466                 body_coords.x      = bbox.x - offset.x;
11467                 body_coords.y      = bbox.y - offset.y;
11468                 body_coords.width  = bbox.width + body_coords.x;
11469                 body_coords.height = bbox.height + body_coords.y;
11470                 body_coords.z1     = bbox.z1;
11471                 body_coords.z2     = bbox.z2;
11472 
11473                 if(body_coords.z2 > body_coords.z1)
11474                 {
11475                     body_coords.z1 -= offset.y;
11476                     body_coords.z2 -= offset.y;
11477                 }
11478 
11479                 entity_coords.x      = ebox.x - offset.x;
11480                 entity_coords.y      = ebox.y - offset.y;
11481                 entity_coords.width  = ebox.width + entity_coords.x;
11482                 entity_coords.height = ebox.height + entity_coords.y;
11483                 entity_coords.z1     = ebox.z1;
11484                 entity_coords.z2     = ebox.z2;
11485 
11486                 if(entity_coords.z2 > entity_coords.z1)
11487                 {
11488                     entity_coords.z1 -= offset.y;
11489                     entity_coords.z2 -= offset.y;
11490                 }
11491 
11492                 attack_coords.x      = abox.x - offset.x;
11493                 attack_coords.y      = abox.y - offset.y;
11494                 attack_coords.width  = abox.width + attack_coords.x;
11495                 attack_coords.height = abox.height + attack_coords.y;
11496 
11497                 if(attack_coords.z2 > attack_coords.z1)
11498                 {
11499                     attack_coords.z1 -= offset.y;
11500                     attack_coords.z2 -= offset.y;
11501                 }
11502 
11503                 //attack.coords.z1 = abox.z1;
11504                 if(platform[PLATFORM_X] == PLATFORM_DEFAULT_X) // old style
11505                 {
11506                     platform_con[PLATFORM_X] = 0;
11507                     platform_con[PLATFORM_Z] = 3;
11508                     platform_con[PLATFORM_UPPERLEFT] = platform[PLATFORM_UPPERLEFT] - offset.x;
11509                     platform_con[PLATFORM_LOWERLEFT] = platform[PLATFORM_LOWERLEFT] - offset.x;
11510                     platform_con[PLATFORM_UPPERRIGHT] = platform[PLATFORM_UPPERRIGHT] - offset.x;
11511                     platform_con[PLATFORM_LOWERRIGHT] = platform[PLATFORM_LOWERRIGHT] - offset.x;
11512                     platform_con[PLATFORM_DEPTH] = platform[PLATFORM_DEPTH] + 3;
11513                 }
11514                 else // wall style
11515                 {
11516                     platform_con[PLATFORM_X] = platform[PLATFORM_X] - offset.x;
11517                     platform_con[PLATFORM_Z] = platform[PLATFORM_Z] - offset.y;
11518                     platform_con[PLATFORM_UPPERLEFT] = platform[PLATFORM_UPPERLEFT];
11519                     platform_con[PLATFORM_LOWERLEFT] = platform[PLATFORM_LOWERLEFT];
11520                     platform_con[PLATFORM_UPPERRIGHT] = platform[PLATFORM_UPPERRIGHT];
11521                     platform_con[PLATFORM_LOWERRIGHT] = platform[PLATFORM_LOWERRIGHT];
11522                     platform_con[PLATFORM_DEPTH] = platform[PLATFORM_DEPTH];
11523                 }
11524                 platform_con[PLATFORM_HEIGHT] = platform[PLATFORM_HEIGHT];
11525                 if(shadow_set)
11526                 {
11527                     shadow_coords[0] = shadow_xz[0] - offset.x;
11528                     shadow_coords[1] = shadow_xz[1] - offset.y;
11529                 }
11530                 else
11531                 {
11532                     shadow_coords[0] = shadow_coords[1] = 0;
11533                 }
11534 
11535                 if(drawmethod.flag)
11536                 {
11537                     dm = drawmethod;
11538                     if(dm.clipw)
11539                     {
11540                         dm.clipx -= offset.x;
11541                         dm.clipy -= offset.y;
11542                     }
11543                 }
11544                 else
11545                 {
11546                     dm.flag = 0;
11547                 }
11548 
11549                 curframe = addframe(newanim, index, framecount, delay, idle,
11550                                     &ebox_con, &bbox_con, &attack, &move, platform_con,
11551                                     frameshadow, shadow_coords, soundtoplay,
11552                                     &dm, &offset, &recursive, &attack_coords,
11553                                     &body_coords, &entity_coords);
11554 
11555                 soundtoplay = -1;
11556                 frm_id = -1;
11557             }
11558             break;
11559             case CMD_MODEL_ALPHAMASK:
11560                 if(!newanim)
11561                 {
11562                     shutdownmessage = "Cannot add alpha mask: animation not specified!";
11563                     goto lCleanup;
11564                 }
11565                 if(maskindex >= 0)
11566                 {
11567                     shutdownmessage = "Cannot add alpha mask: a mask has already been specified for this frame!";
11568                     goto lCleanup;
11569                 }
11570                 value = GET_ARG(1);
11571                 //printf("frame count: %d\n",framecount);
11572                 //printf("Load sprite '%s'...\n", value);
11573                 index = loadsprite(value, offset.x, offset.y, PIXEL_8); //don't use palette for the mask
11574                 maskindex = index;
11575                 break;
11576             case CMD_MODEL_FLIPFRAME:
11577                 newanim->flipframe = GET_FRAME_ARG(1);
11578                 break;
11579             case CMD_MODEL_FOLLOWANIM:
11580                 newanim->followup.animation = GET_INT_ARG(1);
11581                 if(newanim->followup.animation > max_follows)
11582                 {
11583                     newanim->followup.animation = max_follows;
11584                 }
11585                 break;
11586             case CMD_MODEL_FOLLOWCOND:
11587                 newanim->followup.condition = GET_INT_ARG(1);
11588                 break;
11589             case CMD_MODEL_COUNTERRANGE:
11590                 newanim->counterrange    = malloc(sizeof(*newanim->counterrange));
11591                 memset(newanim->counterrange, 0, sizeof(*newanim->counterrange));
11592 
11593                 newanim->counterrange->frame.min    = GET_FRAME_ARG(1);
11594                 newanim->counterrange->frame.max    = GET_FRAME_ARG(2);
11595                 newanim->counterrange->condition    = GET_INT_ARG(3);
11596                 newanim->counterrange->damaged      = GET_INT_ARG(4);
11597                 break;
11598             case CMD_MODEL_WEAPONFRAME:
11599                 if(!newanim->weaponframe)
11600                 {
11601                     newanim->weaponframe = malloc(sizeof(*newanim->weaponframe) * 3);
11602                 }
11603                 newanim->weaponframe[0] = GET_FRAME_ARG(1);
11604                 newanim->weaponframe[1] = GET_INT_ARG(2);
11605                 newanim->weaponframe[2] = GET_INT_ARG(3);
11606                 break;
11607             case CMD_MODEL_QUAKEFRAME:
11608                 newanim->quakeframe.framestart  = GET_FRAME_ARG(1);
11609                 newanim->quakeframe.repeat      = GET_INT_ARG(2);
11610                 newanim->quakeframe.v           = GET_INT_ARG(3);
11611                 newanim->quakeframe.cnt         = 0;
11612                 break;
11613             case CMD_MODEL_SUBENTITY:
11614             case CMD_MODEL_CUSTENTITY:
11615                 value = GET_ARG(1);
11616                 if(value[0])
11617                 {
11618                     newanim->subentity = get_cached_model_index(value);
11619                 }
11620                 break;
11621             case CMD_MODEL_SPAWNFRAME:
11622                 newanim->spawnframe    = malloc(5 * sizeof(*newanim->spawnframe));
11623                 memset(newanim->spawnframe, 0, 5 * sizeof(*newanim->spawnframe));
11624                 newanim->spawnframe[0] = GET_FRAME_ARG(1);
11625                 newanim->spawnframe[1] = GET_FLOAT_ARG(2);
11626                 newanim->spawnframe[2] = GET_FLOAT_ARG(3);
11627                 newanim->spawnframe[3] = GET_FLOAT_ARG(4);
11628                 newanim->spawnframe[4] = GET_FLOAT_ARG(5);
11629                 break;
11630             case CMD_MODEL_SUMMONFRAME:
11631                 newanim->summonframe    = malloc(5 * sizeof(*newanim->summonframe));
11632                 memset(newanim->summonframe, 0, 5 * sizeof(*newanim->summonframe));
11633                 newanim->summonframe[0] = GET_FRAME_ARG(1);
11634                 newanim->summonframe[1] = GET_FLOAT_ARG(2);
11635                 newanim->summonframe[2] = GET_FLOAT_ARG(3);
11636                 newanim->summonframe[3] = GET_FLOAT_ARG(4);
11637                 newanim->summonframe[4] = GET_FLOAT_ARG(5);
11638                 break;
11639             case CMD_MODEL_STAR_VELOCITY:
11640                 newanim->starvelocity    = malloc(3 * sizeof(*newanim->starvelocity));
11641                 memset(newanim->starvelocity, 0, 3 * sizeof(*newanim->starvelocity));
11642                 newanim->starvelocity[0] = GET_FLOAT_ARG(1);
11643                 newanim->starvelocity[1] = GET_FLOAT_ARG(2);
11644                 newanim->starvelocity[2] = GET_FLOAT_ARG(3);
11645                 break;
11646             case CMD_MODEL_UNSUMMONFRAME:
11647                 newanim->unsummonframe = GET_FRAME_ARG(1);
11648                 break;
11649             case CMD_MODEL_NOHITHEAD:
11650                 value = GET_ARG(1);
11651                 newchar->nohithead = atoi(value);
11652                 break;
11653             case CMD_MODEL_AT_SCRIPT:
11654                 if(!scriptbuf[0])  // if empty, paste the main function text here
11655                 {
11656                     buffer_append(&scriptbuf, pre_text, 0xffffff, &sbsize, &scriptlen);
11657                 }
11658                 scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars
11659                 scriptlen = strlen(scriptbuf);
11660                 if(ani_id >= 0)
11661                 {
11662                     if(script_id != ani_id)  // if expression 1
11663                     {
11664                         sprintf(namebuf, ifid_text, newanim->index);
11665                         buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11666                         script_id = ani_id;
11667                     }
11668                     scriptbuf[scriptlen - strclen(endifid_text)] = 0; // cut last chars
11669                     scriptlen = strlen(scriptbuf);
11670                 }
11671                 while(!starts_with(buf + pos, "@script"))
11672                 {
11673                     pos++;
11674                 }
11675                 pos += strclen("@script");
11676                 len = 0;
11677                 while(!starts_with(buf + pos, "@end_script"))
11678                 {
11679                     len++;
11680                     pos++;
11681                 }
11682                 buffer_append(&scriptbuf, buf + pos - len, len, &sbsize, &scriptlen);
11683                 pos += strclen("@end_script");
11684 
11685                 if(ani_id >= 0)
11686                 {
11687                     buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen);// put back last  chars
11688                 }
11689                 buffer_append(&scriptbuf, sur_text, 0xffffff, &sbsize, &scriptlen);// put back last  chars
11690                 break;
11691             case CMD_MODEL_AT_CMD:
11692                 //translate @cmd into script function call
11693                 if(ani_id < 0)
11694                 {
11695                     shutdownmessage = "command '@cmd' must follow an animation!";
11696                     goto lCleanup;
11697                 }
11698                 if(!scriptbuf[0])  // if empty, paste the main function text here
11699                 {
11700                     buffer_append(&scriptbuf, pre_text, 0xffffff, &sbsize, &scriptlen);
11701                 }
11702                 scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars
11703                 scriptlen = strlen(scriptbuf);
11704                 if(script_id != ani_id)  // if expression 1
11705                 {
11706                     sprintf(namebuf, ifid_text, newanim->index);
11707                     buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11708                     script_id = ani_id;
11709                 }
11710                 j = 1;
11711                 value = GET_ARG(j);
11712                 scriptbuf[scriptlen - strclen(endifid_text)] = 0; // cut last chars
11713                 scriptlen = strlen(scriptbuf);
11714                 if(value && value[0])
11715                 {
11716                     /*
11717                      //no_cmd_compatible will try to optimize if(frame==n)
11718                      //which means merging extra if statements within the same frame
11719                      //some old mod will have problem if this is enabled, however.
11720                      //
11721                      //     @cmd f
11722                      //     @cmd f
11723                      //     frame
11724                      //
11725                      //   When no_cmd_compatible is 1
11726                      //
11727                      //   if(frame==n) {
11728                      //       f();
11729                      //       f();
11730                      //       return;
11731                      //    }
11732                      //
11733                      //    When no_cmd_compatible is 0
11734                      //
11735                      //   if(frame==n) {
11736                      //       f();
11737                      //    }
11738                      //   if(frame==n) {
11739                      //       f();
11740                      //    }
11741                      */
11742                     if(!no_cmd_compatible || frm_id != curframe)
11743                     {
11744                         sprintf(namebuf, if_text, curframe);//only execute in current frame
11745                         buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11746                         frm_id = curframe;
11747                     }
11748                     else //no_cmd_compatible==1
11749                     {
11750                         scriptbuf[scriptlen - strclen(endif_text)] = 0; // cut last chars
11751                         scriptlen = strlen(scriptbuf);
11752                         scriptbuf[scriptlen - strclen(endif_return_text)] = 0; // cut last chars
11753                         scriptlen = strlen(scriptbuf);
11754                     }
11755                     sprintf(namebuf, call_text, value);
11756                     buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11757 
11758                     do  //argument and comma
11759                     {
11760                         j++;
11761                         value = GET_ARG(j);
11762                         if(value && value[0])
11763                         {
11764                             if(j != 2)
11765                             {
11766                                 buffer_append(&scriptbuf, comma_text, 0xffffff, &sbsize, &scriptlen);
11767                             }
11768                             buffer_append(&scriptbuf, value, 0xffffff, &sbsize, &scriptlen);
11769                         }
11770                     }
11771                     while(value && value[0]);
11772                 }
11773 
11774                 buffer_append(&scriptbuf, endcall_text, 0xffffff, &sbsize, &scriptlen);
11775                 if(no_cmd_compatible)
11776                 {
11777                     buffer_append(&scriptbuf, endif_return_text, 0xffffff, &sbsize, &scriptlen);    //return
11778                 }
11779                 buffer_append(&scriptbuf, endif_text, 0xffffff, &sbsize, &scriptlen);//end of if
11780                 buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen); // put back last  chars
11781                 buffer_append(&scriptbuf, sur_text, 0xffffff, &sbsize, &scriptlen); // put back last  chars
11782                 break;
11783             default:
11784                 if(command && command[0])
11785                 {
11786                     if(!handle_txt_include(command, &arglist, &filename, fnbuf, &buf, &pos, &size))
11787                     {
11788                         printf("Command '%s' not understood in file '%s'!\n", command, filename);
11789                     }
11790                 }
11791             }
11792 
11793         }
11794         // Go to next line
11795         pos += getNewLineStart(buf + pos);
11796     }
11797 
11798 
11799     tempInt = 1;
11800 
11801     if(scriptbuf && animscriptbuf && scriptbuf[0] && animscriptbuf[0])
11802     {
11803         writeToScriptLog("\n#### animationscript function main #####\n# ");
11804         writeToScriptLog(filename);
11805         writeToScriptLog("\n########################################\n");
11806         writeToScriptLog(scriptbuf);
11807 
11808         lcmScriptDeleteMain(&scriptbuf);
11809         lcmScriptAddMain(&animscriptbuf);
11810         lcmScriptJoinMain(&animscriptbuf,scriptbuf);
11811 
11812         if(!Script_IsInitialized(newchar->scripts->animation_script))
11813         {
11814             Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0);
11815         }
11816         tempInt = Script_AppendText(newchar->scripts->animation_script, animscriptbuf, filename);
11817     }
11818     else if(animscriptbuf && animscriptbuf[0])
11819     {
11820         lcmScriptAddMain(&animscriptbuf);
11821 
11822         if(!Script_IsInitialized(newchar->scripts->animation_script))
11823         {
11824             Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0);
11825         }
11826         tempInt = Script_AppendText(newchar->scripts->animation_script, animscriptbuf, filename);
11827     }
11828     else if(scriptbuf && scriptbuf[0])
11829     {
11830         //printf("\n%s\n", scriptbuf);
11831         if(!Script_IsInitialized(newchar->scripts->animation_script))
11832         {
11833             Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0);
11834         }
11835         tempInt = Script_AppendText(newchar->scripts->animation_script, scriptbuf, filename);
11836         //Interpreter_OutputPCode(newchar->scripts->animation_script.pinterpreter, "code");
11837         writeToScriptLog("\n#### animationscript function main #####\n# ");
11838         writeToScriptLog(filename);
11839         writeToScriptLog("\n########################################\n");
11840         writeToScriptLog(scriptbuf);
11841     }
11842 
11843     if(!newchar->isSubclassed)
11844     {
11845         Script_Compile(newchar->scripts->animation_script);
11846     }
11847 
11848     if(!tempInt)// parse script failed
11849     {
11850         shutdownmessage = "Error parsing function main of animation script in file '%s'!";
11851         goto lCleanup;
11852     }
11853 
11854     // We need a little more work to initialize the new A.I. types if they are not loaded from file
11855     if(newchar->aiattack == -1)
11856     {
11857         newchar->aiattack = 0;
11858     }
11859     if(newchar->aimove == -1)
11860     {
11861         newchar->aimove = 0;
11862     }
11863     //if(!newchar->offscreenkill) newchar->offscreenkill = 1000;
11864 
11865     //temporary patch for conflicting moves
11866     if(newchar->animation[ANI_FREESPECIAL] && !is_set(newchar, ANI_FREESPECIAL))
11867     {
11868         alloc_specials(newchar);
11869         newchar->special[newchar->specials_loaded].input[0] = FLAG_FORWARD;
11870         newchar->special[newchar->specials_loaded].input[1] = FLAG_FORWARD;
11871         newchar->special[newchar->specials_loaded].input[2] = FLAG_ATTACK;
11872         newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL;
11873         newchar->special[newchar->specials_loaded].steps = 3;
11874         newchar->specials_loaded++;
11875     }
11876     if(newchar->animation[ANI_FREESPECIAL2] && !is_set(newchar, ANI_FREESPECIAL2))
11877     {
11878         alloc_specials(newchar);
11879         newchar->special[newchar->specials_loaded].input[0] = FLAG_MOVEDOWN;
11880         newchar->special[newchar->specials_loaded].input[1] = FLAG_MOVEDOWN;
11881         newchar->special[newchar->specials_loaded].input[2] = FLAG_ATTACK;
11882         newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL2;
11883         newchar->special[newchar->specials_loaded].steps = 3;
11884         newchar->specials_loaded++;
11885     }
11886     if(newchar->animation[ANI_FREESPECIAL3] && !is_set(newchar, ANI_FREESPECIAL3))
11887     {
11888         alloc_specials(newchar);
11889         newchar->special[newchar->specials_loaded].input[0] = FLAG_MOVEUP;
11890         newchar->special[newchar->specials_loaded].input[1] = FLAG_MOVEUP;
11891         newchar->special[newchar->specials_loaded].input[2] = FLAG_ATTACK;
11892         newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL3;
11893         newchar->special[newchar->specials_loaded].steps = 3;
11894         newchar->specials_loaded++;
11895     }
11896 
11897     if(newchar->risetime.rise == -1)
11898     {
11899         if(newchar->type == TYPE_PLAYER)
11900         {
11901             if(newchar->animation[ANI_RISEATTACK])
11902             {
11903                 newchar->risetime.rise = GAME_SPEED / 2;
11904             }
11905             else
11906             {
11907                 newchar->risetime.rise = GAME_SPEED;
11908             }
11909         }
11910         else if(newchar->type == TYPE_ENEMY || newchar->type == TYPE_NPC)
11911         {
11912             newchar->risetime.rise = 0;
11913         }
11914     }
11915 
11916     if(newchar->hostile < 0) // not been initialized, so initialize it
11917     {
11918         switch (newchar->type)
11919         {
11920         default:
11921             //Do nothing.
11922             break;
11923         case TYPE_ENEMY:
11924             newchar->hostile = TYPE_PLAYER ;
11925             break;
11926         case TYPE_PLAYER: // dont really needed, since you don't need A.I. control for players
11927             newchar->hostile = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
11928             break;
11929         case TYPE_TRAP:
11930             newchar->hostile  = TYPE_ENEMY | TYPE_PLAYER;
11931         case TYPE_OBSTACLE:
11932             newchar->hostile = 0;
11933             break;
11934         case TYPE_SHOT:  // only target enemies
11935             newchar->hostile = TYPE_ENEMY ;
11936             break;
11937         case TYPE_NPC: // default npc behivior
11938             newchar->hostile = TYPE_ENEMY ;
11939             break;
11940         }
11941     }
11942 
11943     if(newchar->candamage < 0) // not been initialized, so initialize it
11944     {
11945         switch (newchar->type)
11946         {
11947         default:
11948             //Do nothing.
11949             break;
11950         case TYPE_ENEMY:
11951             newchar->candamage = TYPE_PLAYER | TYPE_SHOT;
11952             if(newchar->subtype == SUBTYPE_ARROW)
11953             {
11954                 newchar->candamage |= TYPE_OBSTACLE;
11955             }
11956             break;
11957         case TYPE_PLAYER:
11958             newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
11959             break;
11960         case TYPE_TRAP:
11961             newchar->candamage  = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11962         case TYPE_OBSTACLE:
11963             newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
11964             break;
11965         case TYPE_SHOT:
11966             newchar->candamage = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11967             break;
11968         case TYPE_NPC:
11969             newchar->candamage = TYPE_ENEMY | TYPE_OBSTACLE;
11970             break;
11971         case TYPE_ITEM:
11972             newchar->candamage = TYPE_PLAYER;
11973             break;
11974         }
11975     }
11976 
11977     if(newchar->projectilehit < 0) // not been initialized, so initialize it
11978     {
11979         switch (newchar->type)
11980         {
11981         default:
11982             //Do nothing.
11983             break;
11984         case TYPE_ENEMY:
11985             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11986             break;
11987         case TYPE_PLAYER:
11988             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11989             break;
11990         case TYPE_TRAP: // hmm, don't really needed
11991             newchar->projectilehit  = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11992         case TYPE_OBSTACLE: // hmm, don't really needed
11993             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11994             break;
11995         case TYPE_SHOT: // hmm, don't really needed
11996             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11997             break;
11998         case TYPE_NPC:
11999             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
12000             break;
12001         }
12002     }
12003 
12004     if(newchar->jumpspeed < 0)
12005     {
12006         newchar->jumpspeed = MAX(newchar->speed, 1);
12007     }
12008 
12009     if(blendfx_is_set == 0)
12010     {
12011         if(newchar->alpha)
12012         {
12013             blendfx[newchar->alpha - 1] = 1;
12014         }
12015         if(newchar->gfxshadow || newchar->shadow)
12016         {
12017             blendfx[BLEND_MULTIPLY] = 1;
12018         }
12019     }
12020 
12021     // we need to convert 8bit colourmap into 32bit palette
12022     if(pixelformat == PIXEL_x8)
12023     {
12024         convert_map_to_palette(newchar, mapflag);
12025     }
12026 
12027 lCleanup:
12028 
12029     if(buf != NULL)
12030     {
12031         free(buf);
12032         buf = NULL;
12033     }
12034     if(scriptbuf)
12035     {
12036         free(scriptbuf);
12037         scriptbuf = NULL;
12038     }
12039     if(animscriptbuf)
12040     {
12041         free(animscriptbuf);
12042         animscriptbuf = NULL;
12043     }
12044     if(mapflag)
12045     {
12046         free(mapflag);
12047         mapflag = NULL;
12048     }
12049 
12050     if(!shutdownmessage)
12051     {
12052         return newchar;
12053     }
12054 
12055     borShutdown(1, "Fatal Error in load_cached_model, file: %s, line %d, message: %s\n", filename, line, shutdownmessage);
12056     return NULL;
12057 
12058     #undef LOG_CMD_TITLE
12059 }
12060 
12061 
12062 
is_set(s_model * model,int m)12063 int is_set(s_model *model, int m)      // New function to determine if a freespecial has been set
12064 {
12065     int i;
12066 
12067     for(i = 0; i < model->specials_loaded; i++)
12068     {
12069         if(model->special[i].anim == m)
12070         {
12071             return 1;
12072         }
12073     }
12074 
12075     return 0;
12076 }
12077 
load_script_setting()12078 int load_script_setting()
12079 {
12080     char *filename = "data/script.txt";
12081     char *buf, *command;
12082     ptrdiff_t pos = 0;
12083     size_t size = 0;
12084     ArgList arglist;
12085     char argbuf[MAX_ARG_LEN + 1] = "";
12086 
12087     if(buffer_pakfile(filename, &buf, &size) != 1)
12088     {
12089         return 0;
12090     }
12091 
12092     while(pos < size)
12093     {
12094         if(ParseArgs(&arglist, buf + pos, argbuf))
12095         {
12096             command = GET_ARG(0);
12097             if(command && command[0])
12098             {
12099                 if(stricmp(command, "maxscriptvars") == 0) // each script can have a variable list that can be accessed by index
12100                 {
12101                     max_script_vars = GET_INT_ARG(1) ;
12102                     if(max_script_vars < 0)
12103                     {
12104                         max_script_vars = 0;
12105                     }
12106                 }
12107                 else if(stricmp(command, "maxentityvars") == 0) // each entity can have a variable list that can be accessed by index
12108                 {
12109                     max_entity_vars = GET_INT_ARG(1) ;
12110                     if(max_entity_vars < 0)
12111                     {
12112                         max_entity_vars = 0;
12113                     }
12114                 }
12115                 else if(stricmp(command, "maxindexedvars") == 0) // a global variable list that can be accessed by index
12116                 {
12117                     max_indexed_vars = GET_INT_ARG(1);
12118                     if(max_indexed_vars < 0)
12119                     {
12120                         max_indexed_vars = 0;
12121                     }
12122                 }
12123                 else if(stricmp(command, "keyscriptrate") == 0) // Rate that keyscripts fire when holding a key.
12124                 {
12125                     keyscriptrate = GET_INT_ARG(1);
12126                 }
12127                 else if(stricmp(command, "alwaysupdate") == 0) //execute update script whenever update() is called
12128                 {
12129                     alwaysupdate = GET_INT_ARG(1);
12130                 }
12131                 else if(stricmp(command, "nonestedscript") == 0) // don't call a script if it is being executed
12132                 {
12133                     no_nested_script = GET_INT_ARG(1);
12134                 }
12135                 else if(stricmp(command, "nocmdcompatible") == 0) // don't call a script if it is being executed
12136                 {
12137                     no_cmd_compatible = GET_INT_ARG(1);
12138                 }
12139             }
12140         }
12141         // Go to next line
12142         pos += getNewLineStart(buf + pos);
12143     }
12144 
12145     if(buf != NULL)
12146     {
12147         free(buf);
12148         buf = NULL;
12149     }
12150     return 1;
12151 }
12152 
load_model_constants()12153 void load_model_constants()
12154 {
12155     char filename[MAX_BUFFER_LEN] = "data/models.txt";
12156     int i;
12157     char *buf;
12158     size_t size;
12159     ptrdiff_t pos;
12160     char *command;
12161     int line = 0;
12162     int maxanim = MAX_ANIS; // temporary counter
12163     ArgList arglist;
12164     char argbuf[MAX_ARG_LEN + 1] = "";
12165     modelstxtCommands cmd;
12166 
12167     // reload default values
12168     max_idles        = MAX_IDLES;
12169     max_walks        = MAX_WALKS;
12170     max_ups          = MAX_UPS;
12171     max_downs        = MAX_DOWNS;
12172     max_backwalks    = MAX_BACKWALKS;
12173     max_attack_types = MAX_ATKS;
12174     max_freespecials = MAX_SPECIALS;
12175     max_follows      = MAX_FOLLOWS;
12176     max_attacks      = MAX_ATTACKS;
12177     max_animations   = MAX_ANIS;
12178 
12179     max_collisons    = MAX_COLLISIONS;
12180 
12181     // free old values
12182     if(animspecials)
12183     {
12184         free(animspecials);
12185         animspecials = NULL;
12186     }
12187     if(animattacks)
12188     {
12189         free(animattacks);
12190         animattacks = NULL;
12191     }
12192     if(animfollows)
12193     {
12194         free(animfollows);
12195         animfollows = NULL;
12196     }
12197     if(animpains)
12198     {
12199         free(animpains);
12200         animpains = NULL;
12201     }
12202     if(animbackpains)
12203     {
12204         free(animbackpains);
12205         animbackpains = NULL;
12206     }
12207     if(animfalls)
12208     {
12209         free(animfalls);
12210         animfalls = NULL;
12211     }
12212     if(animbackfalls)
12213     {
12214         free(animbackfalls);
12215         animbackfalls = NULL;
12216     }
12217     if(animrises)
12218     {
12219         free(animrises);
12220         animrises = NULL;
12221     }
12222     if(animbackrises)
12223     {
12224         free(animbackrises);
12225         animbackrises = NULL;
12226     }
12227     if(animriseattacks)
12228     {
12229         free(animriseattacks);
12230         animriseattacks = NULL;
12231     }
12232     if(animbackriseattacks)
12233     {
12234         free(animbackriseattacks);
12235         animbackriseattacks = NULL;
12236     }
12237     if(animblkpains)
12238     {
12239         free(animblkpains);
12240         animblkpains = NULL;
12241     }
12242     if(animbackblkpains)
12243     {
12244         free(animbackblkpains);
12245         animbackblkpains = NULL;
12246     }
12247     if(animdies)
12248     {
12249         free(animdies);
12250         animdies = NULL;
12251     }
12252     if(animbackdies)
12253     {
12254         free(animbackdies);
12255         animbackdies = NULL;
12256     }
12257     if(animwalks)
12258     {
12259         free(animwalks);
12260         animwalks = NULL;
12261     }
12262     if(animbackwalks)
12263     {
12264         free(animbackwalks);
12265         animbackwalks = NULL;
12266     }
12267     if(animidles)
12268     {
12269         free(animidles);
12270         animidles = NULL;
12271     }
12272     if(animups)
12273     {
12274         free(animups);
12275         animups = NULL;
12276     }
12277     if(animdowns)
12278     {
12279         free(animdowns);
12280         animdowns = NULL;
12281     }
12282 
12283     // Read file
12284     if(buffer_pakfile(filename, &buf, &size) != 1)
12285     {
12286         borShutdown(1, "Error loading model list from %s", filename);
12287     }
12288 
12289     pos = 0;
12290     while(pos < size) // peek global settings
12291     {
12292         line++;
12293         if(ParseArgs(&arglist, buf + pos, argbuf))
12294         {
12295             command = GET_ARG(0);
12296             cmd = (modelstxtCommands)getModelCommand(modelstxtcmdlist, command);
12297             switch(cmd)
12298             {
12299             case CMD_MODELSTXT_MAX_COLLISIONS:
12300                 // max collision_attack boxes.
12301                 max_collisons = GET_INT_ARG(1);
12302                 if(max_collisons < MAX_COLLISIONS)
12303                 {
12304                     max_collisons = MAX_COLLISIONS;
12305                 }
12306                 break;
12307             case CMD_MODELSTXT_MAXIDLES:
12308                 // max idle stances
12309                 max_idles = GET_INT_ARG(1);
12310                 if(max_idles < MAX_IDLES)
12311                 {
12312                     max_idles = MAX_IDLES;
12313                 }
12314                 break;
12315             case CMD_MODELSTXT_MAXWALKS:
12316                 max_walks = GET_INT_ARG(1);
12317                 if(max_walks < MAX_WALKS)
12318                 {
12319                     max_walks = MAX_WALKS;
12320                 }
12321                 break;
12322             case CMD_MODELSTXT_MAXBACKWALKS:
12323                 // max backward walks
12324                 max_backwalks = GET_INT_ARG(1);
12325                 if(max_backwalks < MAX_BACKWALKS)
12326                 {
12327                     max_backwalks = MAX_BACKWALKS;
12328                 }
12329                 break;
12330             case CMD_MODELSTXT_MAXUPS:
12331                 // max up walks
12332                 max_ups = GET_INT_ARG(1);
12333                 if(max_ups < MAX_UPS)
12334                 {
12335                     max_ups = MAX_UPS;
12336                 }
12337                 break;
12338             case CMD_MODELSTXT_MAXDOWNS:
12339                 // max down walks
12340                 max_downs = GET_INT_ARG(1);
12341                 if(max_downs < MAX_DOWNS)
12342                 {
12343                     max_downs = MAX_DOWNS;
12344                 }
12345                 break;
12346             case CMD_MODELSTXT_MAXATTACKTYPES:
12347                 // max attacktype/pain/fall/die
12348                 max_attack_types = GET_INT_ARG(1) + STA_ATKS;
12349                 if(max_attack_types < MAX_ATKS)
12350                 {
12351                     max_attack_types = MAX_ATKS;
12352                 }
12353                 break;
12354             case CMD_MODELSTXT_MAXFOLLOWS:
12355                 // max follow-ups
12356                 max_follows = GET_INT_ARG(1);
12357                 if(max_follows < MAX_FOLLOWS)
12358                 {
12359                     max_follows = MAX_FOLLOWS;
12360                 }
12361                 break;
12362             case CMD_MODELSTXT_MAXFREESPECIALS:
12363                 // max freespecials
12364                 max_freespecials = GET_INT_ARG(1);
12365                 if(max_freespecials < MAX_SPECIALS)
12366                 {
12367                     max_freespecials = MAX_SPECIALS;
12368                 }
12369                 break;
12370             case CMD_MODELSTXT_MAXATTACKS:
12371                 max_attacks = GET_INT_ARG(1);
12372                 if(max_attacks < MAX_ATTACKS)
12373                 {
12374                     max_attacks = MAX_ATTACKS;
12375                 }
12376                 break;
12377             default:
12378                 if(cmd >= CMD_MODELSTXT_THE_END)
12379                 {
12380                     printf("command %s not understood in %s, line %d\n", command, filename, line);
12381                 }
12382                 break;
12383             }
12384         }
12385 
12386         // Go to next line
12387         pos += getNewLineStart(buf + pos);
12388     }
12389 
12390     // calculate max animations
12391     max_animations += (max_attack_types - MAX_ATKS) * 12 +// multply by 11: fall/die/pain/backpain/backfalls/backdies/rise/backrise/blockpain/backblockpain/riseattack/backriseattck
12392                       (max_follows - MAX_FOLLOWS) +
12393                       (max_freespecials - MAX_SPECIALS) +
12394                       (max_attacks - MAX_ATTACKS) +
12395                       (max_idles - MAX_IDLES) +
12396                       (max_walks - MAX_WALKS) +
12397                       (max_ups - MAX_UPS) +
12398                       (max_downs - MAX_DOWNS) +
12399                       (max_backwalks - MAX_BACKWALKS);
12400 
12401     // alloc indexed animation ids
12402     animdowns = malloc(sizeof(*animdowns) * max_downs);
12403     animups = malloc(sizeof(*animups) * max_ups);
12404     animbackwalks = malloc(sizeof(*animbackwalks) * max_backwalks);
12405     animwalks = malloc(sizeof(*animwalks) * max_walks);
12406     animidles = malloc(sizeof(*animidles) * max_idles);
12407     animpains = malloc(sizeof(*animpains) * max_attack_types);
12408     animbackpains = malloc(sizeof(*animbackpains) * max_attack_types);
12409     animdies = malloc(sizeof(*animdies) * max_attack_types);
12410     animbackdies = malloc(sizeof(*animbackdies) * max_attack_types);
12411     animfalls = malloc(sizeof(*animfalls) * max_attack_types);
12412     animbackfalls = malloc(sizeof(*animbackfalls) * max_attack_types);
12413     animrises = malloc(sizeof(*animrises) * max_attack_types);
12414     animbackrises = malloc(sizeof(*animbackrises) * max_attack_types);
12415     animriseattacks = malloc(sizeof(*animriseattacks) * max_attack_types);
12416     animbackriseattacks = malloc(sizeof(*animbackriseattacks) * max_attack_types);
12417     animblkpains = malloc(sizeof(*animblkpains) * max_attack_types);
12418     animbackblkpains = malloc(sizeof(*animbackblkpains) * max_attack_types);
12419     animattacks = malloc(sizeof(*animattacks) * max_attacks);
12420     animfollows = malloc(sizeof(*animfollows) * max_follows);
12421     animspecials = malloc(sizeof(*animspecials) * max_freespecials);
12422 
12423     // copy default values and new animation ids
12424     memcpy(animdowns, downs, sizeof(*animdowns)*MAX_DOWNS);
12425     for(i = MAX_DOWNS; i < max_downs; i++)
12426     {
12427         animdowns[i] = maxanim++;
12428     }
12429     memcpy(animups, ups, sizeof(*animups)*MAX_UPS);
12430     for(i = MAX_UPS; i < max_ups; i++)
12431     {
12432         animups[i] = maxanim++;
12433     }
12434     memcpy(animbackwalks, backwalks, sizeof(*animbackwalks)*MAX_BACKWALKS);
12435     for(i = MAX_BACKWALKS; i < max_backwalks; i++)
12436     {
12437         animbackwalks[i] = maxanim++;
12438     }
12439     memcpy(animwalks, walks, sizeof(*animwalks)*MAX_WALKS);
12440     for(i = MAX_WALKS; i < max_walks; i++)
12441     {
12442         animwalks[i] = maxanim++;
12443     }
12444     memcpy(animidles, idles, sizeof(*animidles)*MAX_IDLES);
12445     for(i = MAX_IDLES; i < max_idles; i++)
12446     {
12447         animidles[i] = maxanim++;
12448     }
12449     memcpy(animspecials, freespecials,   sizeof(*animspecials)*MAX_SPECIALS);
12450     for(i = MAX_SPECIALS; i < max_freespecials; i++)
12451     {
12452         animspecials[i] = maxanim++;
12453     }
12454     memcpy(animattacks,  normal_attacks, sizeof(*animattacks)*MAX_ATTACKS);
12455     for(i = MAX_ATTACKS; i < max_attacks; i++)
12456     {
12457         animattacks[i] = maxanim++;
12458     }
12459     memcpy(animfollows,  follows,        sizeof(*animfollows)*MAX_FOLLOWS);
12460     for(i = MAX_FOLLOWS; i < max_follows; i++)
12461     {
12462         animfollows[i] = maxanim++;
12463     }
12464     memcpy(animpains,    pains,          sizeof(*animpains)*MAX_ATKS);
12465     for(i = MAX_ATKS; i < max_attack_types; i++)
12466     {
12467         animpains[i] = maxanim++;
12468     }
12469     memcpy(animbackpains,    backpains,          sizeof(*animbackpains)*MAX_ATKS);
12470     for(i = MAX_ATKS; i < max_attack_types; i++)
12471     {
12472         animbackpains[i] = maxanim++;
12473     }
12474     memcpy(animfalls,    falls,          sizeof(*animfalls)*MAX_ATKS);
12475     for(i = MAX_ATKS; i < max_attack_types; i++)
12476     {
12477         animfalls[i] = maxanim++;
12478     }
12479     memcpy(animbackfalls,    backfalls,          sizeof(*animbackfalls)*MAX_ATKS);
12480     for(i = MAX_ATKS; i < max_attack_types; i++)
12481     {
12482         animbackfalls[i] = maxanim++;
12483     }
12484     memcpy(animrises,    rises,          sizeof(*animrises)*MAX_ATKS);
12485     for(i = MAX_ATKS; i < max_attack_types; i++)
12486     {
12487         animrises[i] = maxanim++;
12488     }
12489     memcpy(animbackrises,    backrises,          sizeof(*animbackrises)*MAX_ATKS);
12490     for(i = MAX_ATKS; i < max_attack_types; i++)
12491     {
12492         animbackrises[i] = maxanim++;
12493     }
12494     memcpy(animriseattacks,    riseattacks,          sizeof(*animriseattacks)*MAX_ATKS);
12495     for(i = MAX_ATKS; i < max_attack_types; i++)
12496     {
12497         animriseattacks[i] = maxanim++;
12498     }
12499     memcpy(animbackriseattacks,    backriseattacks,          sizeof(*animbackriseattacks)*MAX_ATKS);
12500     for(i = MAX_ATKS; i < max_attack_types; i++)
12501     {
12502         animbackriseattacks[i] = maxanim++;
12503     }
12504     memcpy(animblkpains,    blkpains,    sizeof(*animblkpains)*MAX_ATKS);
12505     for(i = MAX_ATKS; i < max_attack_types; i++)
12506     {
12507         animblkpains[i] = maxanim++;
12508     }
12509     memcpy(animbackblkpains,    backblkpains,    sizeof(*animbackblkpains)*MAX_ATKS);
12510     for(i = MAX_ATKS; i < max_attack_types; i++)
12511     {
12512         animbackblkpains[i] = maxanim++;
12513     }
12514     memcpy(animdies,     deaths,         sizeof(*animdies)*MAX_ATKS);
12515     for(i = MAX_ATKS; i < max_attack_types; i++)
12516     {
12517         animdies[i] = maxanim++;
12518     }
12519     memcpy(animbackdies,     backdeaths,         sizeof(*animbackdies)*MAX_ATKS);
12520     for(i = MAX_ATKS; i < max_attack_types; i++)
12521     {
12522         animbackdies[i] = maxanim++;
12523     }
12524 
12525     if(buf)
12526     {
12527         free(buf);
12528     }
12529 }
12530 
12531 // Load / cache all models
load_models()12532 int load_models()
12533 {
12534     char filename[MAX_BUFFER_LEN] = "data/models.txt";
12535     int i;
12536     char *buf;
12537     size_t size;
12538     ptrdiff_t pos;
12539     char *command;
12540     int line = 0;
12541 
12542     char tmpBuff[MAX_BUFFER_LEN] = {""};
12543 
12544     ArgList arglist;
12545     char argbuf[MAX_ARG_LEN + 1] = "";
12546     modelstxtCommands cmd;
12547     int modelLoadCount = 0;
12548 
12549     free_modelcache();
12550 
12551     if(isLoadingScreenTypeBg(loadingbg[0].set))
12552     {
12553         // New alternative background path for PSP
12554         if(custBkgrds != NULL)
12555         {
12556             strcpy(tmpBuff, custBkgrds);
12557             strcat(tmpBuff, "loading");
12558             load_background(tmpBuff);
12559         }
12560         else
12561         {
12562             load_background("data/bgs/loading");
12563         }
12564         standard_palette(1);
12565     }
12566     if(isLoadingScreenTypeBar(loadingbg[0].set))
12567     {
12568         lifebar_colors();
12569         init_colourtable();
12570     }
12571 
12572     update_loading(&loadingbg[0], -1, 1); // initialize the update screen
12573 
12574     if(custModels != NULL)
12575     {
12576         strcpy(filename, "data/");
12577         strcat(filename, custModels);
12578     }
12579 
12580     // Read file
12581     if(buffer_pakfile(filename, &buf, &size) != 1)
12582     {
12583         borShutdown(1, "Error loading model list from %s", filename);
12584     }
12585 
12586     pos = 0;
12587     while(pos < size) // peek global settings
12588     {
12589         line++;
12590         if(ParseArgs(&arglist, buf + pos, argbuf))
12591         {
12592             command = GET_ARG(0);
12593             cmd = (modelstxtCommands)getModelCommand(modelstxtcmdlist, command);
12594             switch(cmd)
12595             {
12596             case CMD_MODELSTXT_COMBODELAY:
12597                 combodelay = GET_INT_ARG(1);
12598                 break;
12599             case CMD_MODELSTXT_MUSIC:
12600                 music(GET_ARG(1), 1, atol(GET_ARG(2)));
12601                 break;
12602             case CMD_MODELSTXT_LOAD:
12603                 // Add path to cache list
12604                 modelLoadCount++;
12605                 cache_model(GET_ARG(1), GET_ARG(2), 1);
12606                 break;
12607             case CMD_MODELSTXT_COLOURSELECT:
12608                 // 6-2-2005 if string for colourselect found
12609                 colourselect =  GET_INT_ARG(1);          //  6-2-2005
12610                 break;
12611             case CMD_MODELSTXT_SPDIRECTION:
12612                 // Select Player Direction for select player screen
12613                 spdirection[0] =  GET_INT_ARG(1);
12614                 spdirection[1] =  GET_INT_ARG(2);
12615                 spdirection[2] =  GET_INT_ARG(3);
12616                 spdirection[3] =  GET_INT_ARG(4);
12617                 break;
12618             case CMD_MODELSTXT_AUTOLAND:
12619                 // New flag to determine if a player auto lands when thrown by another player (2 completely disables the ability to land)
12620                 autoland = GET_INT_ARG(1);
12621                 break;
12622             case CMD_MODELSTXT_NOLOST:
12623                 // this is use for dont lost your weapon if you grab a enemy flag it to 1 to no drop by tails
12624                 nolost = GET_INT_ARG(1);
12625                 break;
12626             case CMD_MODELSTXT_AJSPECIAL:
12627                 // Flag to determine if a + j executes special
12628                 ajspecial = GET_INT_ARG(1);
12629                 break;
12630             case CMD_MODELSTXT_NOCOST:
12631                 // Nocost set in models.txt
12632                 nocost = GET_INT_ARG(1);
12633                 break;
12634             case CMD_MODELSTXT_NOCHEATS:
12635                 //disable cheat option in menu
12636                 forcecheatsoff =  GET_INT_ARG(1);
12637                 break;
12638             case CMD_MODELSTXT_NODEBUG:
12639                 //disable debug option in menu
12640                 nodebugoptions =  GET_INT_ARG(1);
12641                 break;
12642             case CMD_MODELSTXT_NODROPEN:
12643                 nodropen = 1;
12644                 break;
12645             case CMD_MODELSTXT_NODROPSPAWN:
12646                 nodropspawn = 1;
12647                 break;
12648             case CMD_MODELSTXT_KNOW:
12649                 // Just add path to cache list
12650                 cache_model(GET_ARG(1), GET_ARG(2), 0);
12651                 break;
12652             case CMD_MODELSTXT_NOAIRCANCEL:
12653                 noaircancel = GET_INT_ARG(1);
12654                 break;
12655             case CMD_MODELSTXT_NOMAXRUSHRESET:
12656                 nomaxrushreset[4] = GET_INT_ARG(1);
12657                 break;
12658             case CMD_MODELSTXT_MPBLOCK:
12659                 // Take from MP first?
12660                 mpblock = GET_INT_ARG(1);
12661                 break;
12662             case CMD_MODELSTXT_BLOCKRATIO:
12663                 // Nullify or reduce damage?
12664                 blockratio = GET_INT_ARG(1);
12665                 break;
12666             case CMD_MODELSTXT_NOCHIPDEATH:
12667                 nochipdeath = GET_INT_ARG(1);
12668                 break;
12669             case CMD_MODELSTXT_LIFESCORE:
12670                 lifescore =  GET_INT_ARG(1);
12671                 break;
12672             case CMD_MODELSTXT_CREDSCORE:
12673                 // Number of points needed to earn a 1-up
12674                 credscore =  GET_INT_ARG(1);
12675                 break;
12676             case CMD_MODELSTXT_VERSUSDAMAGE:
12677                 // Number of points needed to earn a credit
12678                 versusdamage =  GET_INT_ARG(1);
12679                 if(versusdamage == 0 || versusdamage == 1)
12680                 {
12681                     savedata.mode = versusdamage ^ 1;
12682                 }
12683                 break;
12684             case CMD_MODELSTXT_DROPV:
12685                 default_model_dropv.y =  GET_FLOAT_ARG(1);
12686                 default_model_dropv.x =  GET_FLOAT_ARG(2);
12687                 default_model_dropv.z =  GET_FLOAT_ARG(3);
12688                 break;
12689             case CMD_MODELSTXT_JUMPSPEED:
12690                 default_model_jumpspeed =  GET_FLOAT_ARG(1);
12691                 break;
12692             case CMD_MODELSTXT_JUMPHEIGHT:
12693                 default_model_jumpheight =  GET_FLOAT_ARG(1);
12694                 break;
12695             case CMD_MODELSTXT_GRABDISTANCE:
12696                 default_model_grabdistance =  GET_FLOAT_ARG(1);
12697                 break;
12698             case CMD_MODELSTXT_DEBUG_MNAF:
12699                 move_noatk_factor =  GET_FLOAT_ARG(1);
12700                 break;
12701             case CMD_MODELSTXT_DEBUG_GNAF:
12702                 group_noatk_factor =  GET_FLOAT_ARG(1);
12703                 break;
12704             case CMD_MODELSTXT_DEBUG_ANAF:
12705                 agg_noatk_factor =  GET_FLOAT_ARG(1);
12706                 break;
12707             case CMD_MODELSTXT_DEBUG_MINNA:
12708                 min_noatk_chance =  GET_FLOAT_ARG(1);
12709                 break;
12710             case CMD_MODELSTXT_DEBUG_MAXNA:
12711                 max_noatk_chance =  GET_FLOAT_ARG(1);
12712                 break;
12713             case CMD_MODELSTXT_DEBUG_OSNAF:
12714                 offscreen_noatk_factor =  GET_FLOAT_ARG(1);
12715                 break;
12716             case CMD_MODELSTXT_DEBUG_NAD:
12717                 noatk_duration =  GET_FLOAT_ARG(1);
12718                 break;
12719             default:
12720                 if(cmd >= CMD_MODELSTXT_THE_END)
12721                 {
12722                     printf("command %s not understood in %s, line %d\n", command, filename, line);
12723                 }
12724                 break;
12725             }
12726         }
12727 
12728         // Go to next line
12729         pos += getNewLineStart(buf + pos);
12730     }
12731 
12732     // Defer load_cached_model, so you can define models after their nested model.
12733     printf("\n");
12734 
12735     for(i = 0, pos = 0; i < models_cached; i++)
12736     {
12737         //printf("Checking '%s' '%s'\n", model_cache[i].name, model_cache[i].path);
12738         if(stricmp(model_cache[i].name, "global_model") == 0)
12739         {
12740             global_model = i;
12741         }
12742         if(model_cache[i].loadflag)
12743         {
12744             load_cached_model(model_cache[i].name, "models.txt", 0);
12745             update_loading(&loadingbg[0], ++pos, modelLoadCount);
12746         }
12747     }
12748     printf("\nLoading models...............\tDone!\n");
12749 
12750 
12751     if(buf)
12752     {
12753         free(buf);
12754     }
12755 
12756     return 1;
12757 }
12758 
12759 
12760 
12761 
unload_levelorder()12762 void unload_levelorder()
12763 {
12764     int i, j, t;
12765     s_level_entry *le;
12766     s_set_entry *se;
12767 
12768     if(levelsets)
12769     {
12770         for(i = 0; i < num_difficulties; i++)
12771         {
12772             se = levelsets + i;
12773             if(se->name)
12774             {
12775                 free(se->name);
12776             }
12777             if(se->numlevels)
12778             {
12779                 for(j = 0; j < se->numlevels; j++)
12780                 {
12781                     le = se->levelorder + j;
12782                     if(le->branchname)
12783                     {
12784                         free(le->branchname);
12785                     }
12786                     if(le->filename)
12787                     {
12788                         free(le->filename);
12789                     }
12790                     //if(le->skipselect)
12791                     //{
12792                         for(t = 0; t < MAX_PLAYERS; t++)
12793                         {
12794                             if(le->skipselect[t])
12795                             {
12796                                 free(le->skipselect[t]);
12797                             }
12798                         }
12799                     //}
12800                 }
12801                 free(se->levelorder);
12802             }
12803         }
12804 
12805         free(levelsets);
12806         levelsets = NULL;
12807     }
12808 
12809     num_difficulties = 0;
12810 }
12811 
12812 
12813 
12814 // Add a level to the level order
add_level(char * filename,s_set_entry * set)12815 s_level_entry *add_level(char *filename, s_set_entry *set)
12816 {
12817     s_level_entry *le = NULL;
12818     int Zs[3] = {0, 0, 0};
12819 
12820     if(z_coords[0] > 0)
12821     {
12822         Zs[0] = z_coords[0];
12823     }
12824     else
12825     {
12826         Zs[0] = PLAYER_MIN_Z;
12827     }
12828 
12829     if(z_coords[1] > 0)
12830     {
12831         Zs[1] = z_coords[1];
12832     }
12833     else
12834     {
12835         Zs[1] = PLAYER_MAX_Z;
12836     }
12837 
12838     if(z_coords[2] > 0)
12839     {
12840         Zs[2] = z_coords[2];
12841     }
12842     else
12843     {
12844         Zs[2] = PLAYER_MIN_Z;
12845     }
12846 
12847     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12848     le = set->levelorder + set->numlevels - 1;
12849     memset(le, 0, sizeof(*le));
12850     if(branch_name[0])
12851     {
12852         le->branchname = NAME(branch_name);
12853     }
12854     le->filename = NAME(filename);
12855     le->z_coords[0] = Zs[0];
12856     le->z_coords[1] = Zs[1];
12857     le->z_coords[2] = Zs[2];
12858 
12859     return le;
12860 }
12861 
12862 
12863 
12864 // Add a scene to the level order
add_scene(char * filename,s_set_entry * set)12865 s_level_entry *add_scene(char *filename, s_set_entry *set)
12866 {
12867     s_level_entry *le = NULL;
12868 
12869     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12870     le = set->levelorder + set->numlevels - 1;
12871     memset(le, 0, sizeof(*le));
12872     if(branch_name[0])
12873     {
12874         le->branchname = NAME(branch_name);
12875     }
12876     le->filename = NAME(filename);
12877     le->type = LE_TYPE_CUT_SCENE;
12878     return le;
12879 }
12880 
12881 // Add a select screen file to the level order
add_select(char * filename,s_set_entry * set)12882 s_level_entry *add_select(char *filename, s_set_entry *set)
12883 {
12884     s_level_entry *le = NULL;
12885 
12886     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12887     le = set->levelorder + set->numlevels - 1;
12888     memset(le, 0, sizeof(*le));
12889     if(branch_name[0])
12890     {
12891         le->branchname = NAME(branch_name);
12892     }
12893     le->filename = NAME(filename);
12894     le->type = LE_TYPE_SELECT_SCREEN;
12895     return le;
12896 }
12897 
add_skipselect(ArgList arglist,s_set_entry * set)12898 s_level_entry *add_skipselect(ArgList arglist, s_set_entry *set)
12899 {
12900     s_level_entry *le = NULL;
12901     char *arg;
12902     int i = 0;
12903 
12904     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12905     le = set->levelorder + set->numlevels - 1;
12906     memset(le, 0, sizeof(*le));
12907     if(branch_name[0])
12908     {
12909         le->branchname = NAME(branch_name);
12910     }
12911 
12912     if(arglist.count == 1)
12913     {
12914         le->noselect = 1;
12915     }
12916     else
12917     {
12918         for(i = 0; i < MAX_PLAYERS; i++)
12919         {
12920             if((arg = GET_ARG(i + 1))[0])
12921             {
12922                 le->skipselect[i] = NAME(arg);
12923             }
12924         }
12925     }
12926 
12927     le->type = LE_TYPE_SKIP_SELECT;
12928     return le;
12929 }
12930 
load_skipselect(char * buf,s_set_entry * set)12931 s_level_entry *load_skipselect(char *buf, s_set_entry *set)
12932 {
12933     ArgList arglist;
12934     char argbuf[MAX_ARG_LEN + 1];
12935 
12936     ParseArgs(&arglist, buf, argbuf);
12937 
12938     return add_skipselect(arglist, set);
12939 }
12940 
save_skipselect(char * buf,char skipselect[MAX_PLAYERS][MAX_NAME_LEN])12941 void save_skipselect(char *buf, char skipselect[MAX_PLAYERS][MAX_NAME_LEN])
12942 {
12943     int i = 0;
12944 
12945     strcpy(buf,"skipselect");
12946 
12947     for (i = 0; i < MAX_PLAYERS; i++)
12948     {
12949         strcat(buf," "); strcat(buf,skipselect[i]);
12950     }
12951 
12952     return;
12953 }
12954 
_readbarstatus(char * buf,s_barstatus * pstatus)12955 static void _readbarstatus(char *buf, s_barstatus *pstatus)
12956 {
12957     char *value;
12958     ArgList arglist;
12959     char argbuf[MAX_ARG_LEN + 1] = "";
12960 
12961     ParseArgs(&arglist, buf, argbuf);
12962     if((value = GET_ARG(1))[0])
12963     {
12964         pstatus->size.x       = atoi(value);
12965     }
12966     else
12967     {
12968         return;
12969     }
12970     if((value = GET_ARG(2))[0])
12971     {
12972         pstatus->size.y       = atoi(value);
12973     }
12974     else
12975     {
12976         return;
12977     }
12978     if((value = GET_ARG(3))[0])
12979     {
12980         pstatus->noborder    = atoi(value);
12981     }
12982     else
12983     {
12984         return;
12985     }
12986     if((value = GET_ARG(4))[0])
12987     {
12988         pstatus->type        = atoi(value);
12989     }
12990     else
12991     {
12992         return;
12993     }
12994     if((value = GET_ARG(5))[0])
12995     {
12996         pstatus->orientation = atoi(value);
12997     }
12998     else
12999     {
13000         return;
13001     }
13002     if((value = GET_ARG(6))[0])
13003     {
13004         pstatus->borderlayer = atoi(value);
13005     }
13006     else
13007     {
13008         return;
13009     }
13010     if((value = GET_ARG(7))[0])
13011     {
13012         pstatus->shadowlayer = atoi(value);
13013     }
13014     else
13015     {
13016         return;
13017     }
13018     if((value = GET_ARG(8))[0])
13019     {
13020         pstatus->barlayer    = atoi(value);
13021     }
13022     else
13023     {
13024         return;
13025     }
13026     if((value = GET_ARG(9))[0])
13027     {
13028         pstatus->backlayer   = atoi(value);
13029     }
13030     else
13031     {
13032         return;
13033     }
13034 }
13035 
add_set()13036 s_set_entry *add_set()
13037 {
13038     s_set_entry *set = NULL;
13039     ++num_difficulties;
13040     if(levelsets)
13041     {
13042         levelsets = realloc(levelsets, sizeof(*levelsets) * num_difficulties);
13043     }
13044     else
13045     {
13046         levelsets = calloc(1, sizeof(*levelsets));
13047     }
13048     set = levelsets + num_difficulties - 1;
13049     memset(set, 0, sizeof(*set));
13050     set->maxplayers = defaultmaxplayers;
13051     return set;
13052 }
13053 
13054 // Load list of levels
load_levelorder()13055 void load_levelorder()
13056 {
13057     static const char *defaulterr = "Error in level order: a set must be specified.";
13058 #define CHKDEF if(!set) { errormessage = (char*) defaulterr; goto lCleanup; }
13059     char filename[MAX_BUFFER_LEN] = "";
13060     int i = 0, j = 0, err = 0;
13061     char *buf;
13062     size_t size;
13063     int pos;
13064     s_set_entry *set = NULL;
13065     s_level_entry *le = NULL;
13066     char *command;
13067     char *arg;
13068     char *errormessage = NULL;
13069     int plifeUsed[2]  = {0, 0};
13070     int elifeUsed[2]  = {0, 0};
13071     int piconUsed[2]  = {0, 0};
13072     int piconwUsed[2] = {0, 0};
13073     int eiconUsed[4]  = {0, 0, 0, 0};
13074     int pmpUsed[4]    = {0, 0, 0, 0};
13075     int plifeXused[4] = {0, 0, 0, 0};     // 4-7-2006 New custimizable variable for players 'x'
13076     int plifeNused[4] = {0, 0, 0, 0};     // 4-7-2006 New custimizable variable for players 'lives'
13077     int enameused[4]  = {0, 0, 0, 0};     // 4-7-2006 New custimizable variable for enemy names
13078     int pnameJused[4] = {0, 0, 0, 0};     // 1-8-2006 New custimizable variable for players name Select Hero
13079     int pscoreUsed[4] = {0, 0, 0, 0};     // 1-8-2006 New custimizable variable for players name Select Hero
13080 
13081     ArgList arglist;
13082     char argbuf[MAX_ARG_LEN + 1] = "";
13083     levelOrderCommands cmd;
13084     int line = 0;
13085 
13086     unload_levelorder();
13087 
13088     if(custLevels != NULL)
13089     {
13090         strcpy(filename, "data/");
13091         strcat(filename, custLevels);
13092     }
13093     else
13094     {
13095         strcpy(filename, "data/levels.txt");
13096     }
13097 
13098     // Read file
13099 
13100     if(buffer_pakfile(filename, &buf, &size) != 1)
13101     {
13102         borShutdown(1, "Error loading level list from %s", filename);
13103     }
13104 
13105     // Now interpret the contents of buf line by line
13106     pos = 0;
13107 
13108     // Custom lifebar/timebox/icon positioning and size
13109     picon[0][0] = piconw[0][0] = picon[2][0] = piconw[2][0] = eicon[0][0] = eicon[2][0] = 2;
13110     picon[1][0] = piconw[1][0] = picon[3][0] = piconw[3][0] = eicon[1][0] = eicon[3][0] = 2 + P2_STATS_DIST;
13111     picon[0][1] = piconw[0][1] = picon[1][1] = piconw[1][1] = 2;
13112     picon[2][1] = piconw[2][1] = picon[3][1] = piconw[3][1] = 202;
13113     plife[0][0] = pmp[0][0] = plife[2][0] = pmp[2][0] = elife[0][0] = elife[2][0] = 20;
13114     plife[1][0] = pmp[1][0] = plife[3][0] = pmp[3][0] = elife[1][0] = elife[3][0] = 20 + P2_STATS_DIST;
13115     plife[0][1] = plife[1][1] = 10;
13116     plife[2][1] = plife[3][1] = 210;
13117     pmp[0][1] = pmp[1][1] = 18;
13118     pmp[2][1] = pmp[3][1] = 218;
13119 
13120     memset(psmenu, 0, sizeof(psmenu));
13121 
13122     eicon[0][1] = eicon[1][1] = 19;
13123     eicon[2][1] = eicon[3][1] = 220;
13124     elife[0][1] = elife[1][1] = 27;
13125     elife[2][1] = elife[3][1] = 227;
13126 
13127     timeloc[0] = 149;
13128     timeloc[1] = 4;
13129     timeloc[2] = 21;
13130     timeloc[3] = 20;
13131     timeloc[4] = 0;
13132 
13133     lbarstatus.size.x  = mpbarstatus.size.x = 100;
13134     lbarstatus.size.y  = 5;
13135     mpbarstatus.size.y = 3;
13136     lbarstatus.noborder = mpbarstatus.noborder = 0;
13137 
13138     // Show Complete Default Values
13139     scomplete[0] = 75;
13140     scomplete[1] = 60;
13141     scomplete[2] = 0;
13142     scomplete[3] = 0;
13143     scomplete[4] = 0;
13144     scomplete[5] = 0;
13145 
13146     // Show Complete Y Values
13147     cbonus[0] = lbonus[0] = rbonus[0] = tscore[0] = 10;
13148     cbonus[1] = cbonus[3] = cbonus[5] = cbonus[7] = cbonus[9] = 100;
13149     lbonus[1] = lbonus[3] = lbonus[5] = lbonus[7] = lbonus[9] = 120;
13150     rbonus[1] = rbonus[3] = rbonus[5] = rbonus[7] = rbonus[9] = 140;
13151     tscore[1] = tscore[3] = tscore[5] = tscore[7] = tscore[9] = 160;
13152 
13153     // Show Complete X Values
13154     cbonus[2] = lbonus[2] = rbonus[2] = tscore[2] = 100;
13155     cbonus[4] = lbonus[4] = rbonus[4] = tscore[4] = 155;
13156     cbonus[6] = lbonus[6] = rbonus[6] = tscore[6] = 210;
13157     cbonus[8] = lbonus[8] = rbonus[8] = tscore[8] = 265;
13158 
13159     while(pos < size)
13160     {
13161         line++;
13162         ParseArgs(&arglist, buf + pos, argbuf);
13163         command = GET_ARG(0);
13164         cmd = getLevelOrderCommand(levelordercmdlist, command);
13165         switch(cmd)
13166         {
13167         case CMD_LEVELORDER_BLENDFX:
13168             for(i = 0; i < MAX_BLENDINGS; i++)
13169             {
13170                 if(GET_INT_ARG(i + 1))
13171                 {
13172                     blendfx[i] = 1;
13173                 }
13174                 else
13175                 {
13176                     blendfx[i] = 0;
13177                 }
13178             }
13179             blendfx_is_set = 1;
13180             break;
13181         case CMD_LEVELORDER_SET:
13182             set = add_set();
13183             set->name = NAME(GET_ARG(1));
13184             set->ifcomplete = 0;
13185             set->saveflag  = 1; // default to 1, so the level can be saved
13186             branch_name[0] = 0;
13187             le = NULL;
13188             break;
13189         case CMD_LEVELORDER_IFCOMPLETE:
13190             CHKDEF;
13191             set->ifcomplete = GET_INT_ARG(1);
13192             break;
13193         case CMD_LEVELORDER_SKIPSELECT:
13194             CHKDEF;
13195             le = add_skipselect(arglist, set);
13196             break;
13197         case CMD_LEVELORDER_FILE:
13198             CHKDEF;
13199             le = add_level(GET_ARG(1), set);
13200             break;
13201         case CMD_LEVELORDER_SCENE:
13202             CHKDEF;
13203             le = add_scene(GET_ARG(1), set);
13204             break;
13205         case CMD_LEVELORDER_SELECT:
13206             CHKDEF;
13207             le = add_select(GET_ARG(1), set);
13208             break;
13209         case CMD_LEVELORDER_NEXT:
13210             CHKDEF;
13211             // Set 'gonext' flag of last loaded level
13212             if(le)
13213             {
13214                 le->gonext = 1;
13215             }
13216             break;
13217         case CMD_LEVELORDER_END:
13218             CHKDEF;
13219             // Set endgame flag of last loaded level
13220             if(le)
13221             {
13222                 le->gonext = 2;
13223             }
13224             break;
13225         case CMD_LEVELORDER_LIVES:
13226             // 7-1-2005  credits/lives/singleplayer start here
13227             // used to read the new # of lives/credits from the levels.txt
13228             CHKDEF;
13229             set->lives = GET_INT_ARG(1);
13230             break;
13231         case CMD_LEVELORDER_DISABLEHOF:
13232             CHKDEF;
13233             set->noshowhof = GET_INT_ARG(1);
13234             break;
13235         case CMD_LEVELORDER_DISABLEGAMEOVER:
13236             CHKDEF;
13237             set->noshowgameover = GET_INT_ARG(1);
13238             break;
13239         case CMD_LEVELORDER_CANSAVE:
13240             // 07-12-31
13241             // 0 this set can't be saved
13242             // 1 save level only
13243             // 2 save player info and level, can't choose player in select menu
13244             CHKDEF;
13245             set->saveflag = GET_INT_ARG(1);
13246             break;
13247         case CMD_LEVELORDER_Z:
13248             //    2-10-05  adjust the walkable coordinates
13249             CHKDEF;
13250             z_coords[0] = GET_INT_ARG(1);
13251             z_coords[1] = GET_INT_ARG(2);
13252             z_coords[2] = GET_INT_ARG(3);
13253             break;
13254         case CMD_LEVELORDER_BRANCH:
13255             //    2007-2-22 level branch name
13256             CHKDEF;
13257             strncpy(branch_name, GET_ARG(1), MAX_NAME_LEN);
13258             break;
13259         case CMD_LEVELORDER_P1LIFE:
13260         case CMD_LEVELORDER_P2LIFE:
13261         case CMD_LEVELORDER_P3LIFE:
13262         case CMD_LEVELORDER_P4LIFE:
13263             switch(cmd)
13264             {
13265             case CMD_LEVELORDER_P1LIFE:
13266                 i = 0;
13267                 break;
13268             case CMD_LEVELORDER_P2LIFE:
13269                 i = 1;
13270                 break;
13271             case CMD_LEVELORDER_P3LIFE:
13272                 i = 2;
13273                 plifeUsed[0] = 1;
13274                 break;
13275             case CMD_LEVELORDER_P4LIFE:
13276                 i = 3;
13277                 plifeUsed[1] = 1;
13278                 break;
13279             default:
13280                 assert(0);
13281             }
13282             if((arg = GET_ARG(1))[0])
13283             {
13284                 plife[i][0] = atoi(arg);
13285             }
13286             if((arg = GET_ARG(2))[0])
13287             {
13288                 plife[i][1] = atoi(arg);
13289             }
13290             break;
13291         case CMD_LEVELORDER_P1MP:
13292         case CMD_LEVELORDER_P2MP:
13293         case CMD_LEVELORDER_P3MP:
13294         case CMD_LEVELORDER_P4MP:
13295             switch(cmd)
13296             {
13297             case CMD_LEVELORDER_P1MP:
13298                 i = 0;
13299                 break;
13300             case CMD_LEVELORDER_P2MP:
13301                 i = 1;
13302                 break;
13303             case CMD_LEVELORDER_P3MP:
13304                 i = 2;
13305                 break;
13306             case CMD_LEVELORDER_P4MP:
13307                 i = 3;
13308                 break;
13309             default:
13310                 assert(0);
13311             }
13312             if((arg = GET_ARG(1))[0])
13313             {
13314                 pmp[i][0] = atoi(arg);
13315             }
13316             if((arg = GET_ARG(2))[0])
13317             {
13318                 pmp[i][1] = atoi(arg);
13319             }
13320             pmpUsed[i] = 1;
13321             break;
13322         case CMD_LEVELORDER_P1LIFEX:
13323         case CMD_LEVELORDER_P2LIFEX:
13324         case CMD_LEVELORDER_P3LIFEX:
13325         case CMD_LEVELORDER_P4LIFEX:
13326             switch(cmd)
13327             {
13328             case CMD_LEVELORDER_P1LIFEX:
13329                 j = 0;
13330                 break;
13331             case CMD_LEVELORDER_P2LIFEX:
13332                 j = 1;
13333                 break;
13334             case CMD_LEVELORDER_P3LIFEX:
13335                 j = 2;
13336                 break;
13337             case CMD_LEVELORDER_P4LIFEX:
13338                 j = 3;
13339                 break;
13340             default:
13341                 assert(0);
13342             }
13343             for(i = 0; i < 3; i++)
13344                 if((arg = GET_ARG(i + 1))[0])
13345                 {
13346                     plifeX[j][i] = atoi(arg);
13347                 }
13348             plifeXused[j] = 1;
13349             break;
13350         case CMD_LEVELORDER_P1LIFEN:
13351         case CMD_LEVELORDER_P2LIFEN:
13352         case CMD_LEVELORDER_P3LIFEN:
13353         case CMD_LEVELORDER_P4LIFEN:
13354             switch(cmd)
13355             {
13356             case CMD_LEVELORDER_P1LIFEN:
13357                 j = 0;
13358                 break;
13359             case CMD_LEVELORDER_P2LIFEN:
13360                 j = 1;
13361                 break;
13362             case CMD_LEVELORDER_P3LIFEN:
13363                 j = 2;
13364                 break;
13365             case CMD_LEVELORDER_P4LIFEN:
13366                 j = 3;
13367                 break;
13368             default:
13369                 assert(0);
13370             }
13371             for(i = 0; i < 3; i++)
13372                 if((arg = GET_ARG(i + 1))[0])
13373                 {
13374                     plifeN[j][i] = atoi(arg);
13375                 }
13376             plifeNused[j] = 1;
13377             break;
13378         case CMD_LEVELORDER_E1LIFE:
13379         case CMD_LEVELORDER_E2LIFE:
13380         case CMD_LEVELORDER_E3LIFE:
13381         case CMD_LEVELORDER_E4LIFE:
13382             switch(cmd)
13383             {
13384             case CMD_LEVELORDER_E1LIFE:
13385                 i = 0;
13386                 break;
13387             case CMD_LEVELORDER_E2LIFE:
13388                 i = 1;
13389                 break;
13390             case CMD_LEVELORDER_E3LIFE:
13391                 i = 2;
13392                 elifeUsed[0] = 1;
13393                 break;
13394             case CMD_LEVELORDER_E4LIFE:
13395                 i = 3;
13396                 elifeUsed[1] = 1;
13397                 break;
13398             default:
13399                 assert(0);
13400             }
13401             if((arg = GET_ARG(1))[0])
13402             {
13403                 elife[i][0] = atoi(arg);
13404             }
13405             if((arg = GET_ARG(2))[0])
13406             {
13407                 elife[i][1] = atoi(arg);
13408             }
13409             break;
13410         case CMD_LEVELORDER_P1ICON:
13411         case CMD_LEVELORDER_P2ICON:
13412         case CMD_LEVELORDER_P3ICON:
13413         case CMD_LEVELORDER_P4ICON:
13414             switch(cmd)
13415             {
13416             case CMD_LEVELORDER_P1ICON:
13417                 i = 0;
13418                 break;
13419             case CMD_LEVELORDER_P2ICON:
13420                 i = 1;
13421                 break;
13422             case CMD_LEVELORDER_P3ICON:
13423                 i = 2;
13424                 piconUsed[0] = 1;
13425                 break;
13426             case CMD_LEVELORDER_P4ICON:
13427                 i = 3;
13428                 piconUsed[1] = 1;
13429                 break;
13430             default:
13431                 assert(0);
13432             }
13433             if((arg = GET_ARG(1))[0])
13434             {
13435                 picon[i][0] = atoi(arg);
13436             }
13437             if((arg = GET_ARG(2))[0])
13438             {
13439                 picon[i][1] = atoi(arg);
13440             }
13441             break;
13442         case CMD_LEVELORDER_P1ICONW:
13443         case CMD_LEVELORDER_P2ICONW:
13444         case CMD_LEVELORDER_P3ICONW:
13445         case CMD_LEVELORDER_P4ICONW:
13446             switch(cmd)
13447             {
13448             case CMD_LEVELORDER_P1ICONW:
13449                 i = 0;
13450                 break;
13451             case CMD_LEVELORDER_P2ICONW:
13452                 i = 1;
13453                 break;
13454             case CMD_LEVELORDER_P3ICONW:
13455                 i = 2;
13456                 piconwUsed[0] = 1;
13457                 break;
13458             case CMD_LEVELORDER_P4ICONW:
13459                 i = 3;
13460                 piconwUsed[1] = 1;
13461                 break;
13462             default:
13463                 assert(0);
13464             }
13465             if((arg = GET_ARG(1))[0])
13466             {
13467                 piconw[i][0] = atoi(arg);
13468             }
13469             if((arg = GET_ARG(2))[0])
13470             {
13471                 piconw[i][1] = atoi(arg);
13472             }
13473             break;
13474         case CMD_LEVELORDER_MP1ICON:
13475         case CMD_LEVELORDER_MP2ICON:
13476         case CMD_LEVELORDER_MP3ICON:
13477         case CMD_LEVELORDER_MP4ICON:
13478             switch(cmd)
13479             {
13480             case CMD_LEVELORDER_MP1ICON:
13481                 i = 0;
13482                 break;
13483             case CMD_LEVELORDER_MP2ICON:
13484                 i = 1;
13485                 break;
13486             case CMD_LEVELORDER_MP3ICON:
13487                 i = 2;
13488                 break;
13489             case CMD_LEVELORDER_MP4ICON:
13490                 i = 3;
13491                 break;
13492             default:
13493                 assert(0);
13494             }
13495             if((arg = GET_ARG(1))[0])
13496             {
13497                 mpicon[i][0] = atoi(arg);
13498             }
13499             if((arg = GET_ARG(2))[0])
13500             {
13501                 mpicon[i][1] = atoi(arg);
13502             }
13503             break;
13504         case CMD_LEVELORDER_P1NAMEJ:
13505         case CMD_LEVELORDER_P2NAMEJ:
13506         case CMD_LEVELORDER_P3NAMEJ:
13507         case CMD_LEVELORDER_P4NAMEJ:
13508             switch(cmd)
13509             {
13510             case CMD_LEVELORDER_P1NAMEJ:
13511                 j = 0;
13512                 break;
13513             case CMD_LEVELORDER_P2NAMEJ:
13514                 j = 1;
13515                 break;
13516             case CMD_LEVELORDER_P3NAMEJ:
13517                 j = 2;
13518                 break;
13519             case CMD_LEVELORDER_P4NAMEJ:
13520                 j = 3;
13521                 break;
13522             default:
13523                 assert(0);
13524             }
13525             for(i = 0; i < 7; i++)
13526                 if((arg = GET_ARG(i + 1))[0])
13527                 {
13528                     pnameJ[j][i] = atoi(arg);
13529                 }
13530             pnameJused[j] = 1;
13531             break;
13532         case CMD_LEVELORDER_P1SCORE:
13533         case CMD_LEVELORDER_P2SCORE:
13534         case CMD_LEVELORDER_P3SCORE:
13535         case CMD_LEVELORDER_P4SCORE:
13536             switch(cmd)
13537             {
13538             case CMD_LEVELORDER_P1SCORE:
13539                 j = 0;
13540                 break;
13541             case CMD_LEVELORDER_P2SCORE:
13542                 j = 1;
13543                 break;
13544             case CMD_LEVELORDER_P3SCORE:
13545                 j = 2;
13546                 break;
13547             case CMD_LEVELORDER_P4SCORE:
13548                 j = 3;
13549                 break;
13550             default:
13551                 assert(0);
13552             }
13553             for(i = 0; i < 7; i++)
13554                 if((arg = GET_ARG(i + 1))[0])
13555                 {
13556                     pscore[j][i] = atoi(arg);
13557                 }
13558             pscoreUsed[j] = 1;
13559             break;
13560         case CMD_LEVELORDER_P1SHOOT:
13561         case CMD_LEVELORDER_P2SHOOT:
13562         case CMD_LEVELORDER_P3SHOOT:
13563         case CMD_LEVELORDER_P4SHOOT:
13564             switch(cmd)
13565             {
13566             case CMD_LEVELORDER_P1SHOOT:
13567                 j = 0;
13568                 break;
13569             case CMD_LEVELORDER_P2SHOOT:
13570                 j = 1;
13571                 break;
13572             case CMD_LEVELORDER_P3SHOOT:
13573                 j = 2;
13574                 break;
13575             case CMD_LEVELORDER_P4SHOOT:
13576                 j = 3;
13577                 break;
13578             default:
13579                 assert(0);
13580             }
13581             for(i = 0; i < 3; i++)
13582                 if((arg = GET_ARG(i + 1))[0])
13583                 {
13584                     pshoot[j][i] = atoi(arg);
13585                 }
13586             break;
13587         case CMD_LEVELORDER_P1RUSH:
13588         case CMD_LEVELORDER_P2RUSH:
13589         case CMD_LEVELORDER_P3RUSH:
13590         case CMD_LEVELORDER_P4RUSH:
13591             switch(cmd)
13592             {
13593             case CMD_LEVELORDER_P1RUSH:
13594                 j = 0;
13595                 break;
13596             case CMD_LEVELORDER_P2RUSH:
13597                 j = 1;
13598                 break;
13599             case CMD_LEVELORDER_P3RUSH:
13600                 j = 2;
13601                 break;
13602             case CMD_LEVELORDER_P4RUSH:
13603                 j = 3;
13604                 break;
13605             default:
13606                 assert(0);
13607             }
13608             for(i = 0; i < 8; i++)
13609                 if((arg = GET_ARG(i + 1))[0])
13610                 {
13611                     prush[j][i] = atoi(arg);
13612                 }
13613             break;
13614         case CMD_LEVELORDER_E1ICON:
13615         case CMD_LEVELORDER_E2ICON:
13616         case CMD_LEVELORDER_E3ICON:
13617         case CMD_LEVELORDER_E4ICON:
13618             switch(cmd)
13619             {
13620             case CMD_LEVELORDER_E1ICON:
13621                 i = 0;
13622                 break;
13623             case CMD_LEVELORDER_E2ICON:
13624                 i = 1;
13625                 break;
13626             case CMD_LEVELORDER_E3ICON:
13627                 i = 2;
13628                 eiconUsed[0] = 1;
13629                 break;
13630             case CMD_LEVELORDER_E4ICON:
13631                 i = 3;
13632                 eiconUsed[1] = 1;
13633                 break;
13634             default:
13635                 assert(0);
13636             }
13637             if((arg = GET_ARG(1))[0])
13638             {
13639                 eicon[i][0] = atoi(arg);
13640             }
13641             if((arg = GET_ARG(2))[0])
13642             {
13643                 eicon[i][1] = atoi(arg);
13644             }
13645             break;
13646         case CMD_LEVELORDER_E1NAME:
13647         case CMD_LEVELORDER_E2NAME:
13648         case CMD_LEVELORDER_E3NAME:
13649         case CMD_LEVELORDER_E4NAME:
13650             switch(cmd)
13651             {
13652             case CMD_LEVELORDER_E1NAME:
13653                 j = 0;
13654                 break;
13655             case CMD_LEVELORDER_E2NAME:
13656                 j = 1;
13657                 break;
13658             case CMD_LEVELORDER_E3NAME:
13659                 j = 2;
13660                 break;
13661             case CMD_LEVELORDER_E4NAME:
13662                 j = 3;
13663                 break;
13664             default:
13665                 assert(0);
13666             }
13667             for(i = 0; i < 3; i++)
13668                 if((arg = GET_ARG(i + 1))[0])
13669                 {
13670                     ename[j][i] = atoi(arg);
13671                 }
13672             enameused[j] = 1;
13673             break;
13674         case CMD_LEVELORDER_P1SMENU:
13675         case CMD_LEVELORDER_P2SMENU:
13676         case CMD_LEVELORDER_P3SMENU:
13677         case CMD_LEVELORDER_P4SMENU:
13678             switch(cmd)
13679             {
13680             case CMD_LEVELORDER_P1SMENU:
13681                 j = 0;
13682                 break;
13683             case CMD_LEVELORDER_P2SMENU:
13684                 j = 1;
13685                 break;
13686             case CMD_LEVELORDER_P3SMENU:
13687                 j = 2;
13688                 break;
13689             case CMD_LEVELORDER_P4SMENU:
13690                 j = 3;
13691                 break;
13692             default:
13693                 assert(0);
13694             }
13695             for(i = 0; i < MAX_PLAYERS; i++)
13696                 if((arg = GET_ARG(i + 1))[0])
13697                 {
13698                     psmenu[j][i] = atoi(arg);
13699                 }
13700             break;
13701         case CMD_LEVELORDER_TIMEICON:
13702             strncpy(timeicon_path, GET_ARG(1), 127);
13703             timeicon = loadsprite(timeicon_path, 0, 0, pixelformat);
13704             if((arg = GET_ARG(2))[0])
13705             {
13706                 timeicon_offsets[0] = atoi(arg);
13707             }
13708             if((arg = GET_ARG(3))[0])
13709             {
13710                 timeicon_offsets[1] = atoi(arg);
13711             }
13712             break;
13713         case CMD_LEVELORDER_BGICON:
13714             strncpy(bgicon_path, GET_ARG(1), 127);
13715             bgicon = loadsprite(bgicon_path, 0, 0, pixelformat);
13716             if((arg = GET_ARG(2))[0])
13717             {
13718                 bgicon_offsets[0] = atoi(arg);
13719             }
13720             if((arg = GET_ARG(3))[0])
13721             {
13722                 bgicon_offsets[1] = atoi(arg);
13723             }
13724             if((arg = GET_ARG(4))[0])
13725             {
13726                 bgicon_offsets[2] = atoi(arg);
13727             }
13728             else
13729             {
13730                 bgicon_offsets[2] = HUD_Z / 2;
13731             }
13732             break;
13733         case CMD_LEVELORDER_OLICON:
13734             strncpy(olicon_path, GET_ARG(1), 127);
13735             olicon = loadsprite(olicon_path, 0, 0, pixelformat);
13736             if((arg = GET_ARG(2))[0])
13737             {
13738                 olicon_offsets[0] = atoi(arg);
13739             }
13740             if((arg = GET_ARG(3))[0])
13741             {
13742                 olicon_offsets[1] = atoi(arg);
13743             }
13744             if((arg = GET_ARG(4))[0])
13745             {
13746                 olicon_offsets[2] = atoi(arg);
13747             }
13748             else
13749             {
13750                 olicon_offsets[2] = HUD_Z * 3;
13751             }
13752             break;
13753         case CMD_LEVELORDER_TIMELOC:
13754             for(i = 0; i < 6; i++)
13755                 if((arg = GET_ARG(i + 1))[0])
13756                 {
13757                     timeloc[i] = atoi(arg);
13758                 }
13759             break;
13760         case CMD_LEVELORDER_PAUSEOFFSET:
13761             for(i = 0; i < 7; i++)
13762                 if((arg = GET_ARG(i + 1))[0])
13763                 {
13764                     pauseoffset[i] = atoi(arg);
13765                 }
13766             break;
13767         case CMD_LEVELORDER_LBARSIZE:
13768             _readbarstatus(buf + pos, &lbarstatus);
13769             break;
13770         case CMD_LEVELORDER_OLBARSIZE:
13771             _readbarstatus(buf + pos, &olbarstatus);
13772             break;
13773         case CMD_LEVELORDER_MPBARSIZE:
13774             _readbarstatus(buf + pos, &mpbarstatus);
13775             break;
13776         case CMD_LEVELORDER_LBARTEXT:
13777             for(i = 0; i < 4; i++)
13778                 if((arg = GET_ARG(i + 1))[0])
13779                 {
13780                     lbartext[i] = atoi(arg);
13781                 }
13782             break;
13783         case CMD_LEVELORDER_MPBARTEXT:
13784             for(i = 0; i < 4; i++)
13785                 if((arg = GET_ARG(i + 1))[0])
13786                 {
13787                     mpbartext[i] = atoi(arg);
13788                 }
13789             break;
13790         case CMD_LEVELORDER_SHOWCOMPLETE:
13791             for(i = 0; i < 6; i++)
13792                 if((arg = GET_ARG(i + 1))[0])
13793                 {
13794                     scomplete[i] = atoi(arg);
13795                 }
13796             break;
13797         case CMD_LEVELORDER_CLEARBONUS:
13798             for(i = 0; i < 10; i++)
13799                 if((arg = GET_ARG(i + 1))[0])
13800                 {
13801                     cbonus[i] = atoi(arg);
13802                 }
13803             break;
13804         case CMD_LEVELORDER_RUSHBONUS:
13805             for(i = 0; i < 10; i++)
13806                 if((arg = GET_ARG(i + 1))[0])
13807                 {
13808                     rbonus[i] = atoi(arg);
13809                 }
13810             break;
13811         case CMD_LEVELORDER_LIFEBONUS:
13812             for(i = 0; i < 10; i++)
13813                 if((arg = GET_ARG(i + 1))[0])
13814                 {
13815                     lbonus[i] = atoi(arg);
13816                 }
13817             break;
13818         case CMD_LEVELORDER_SCBONUSES:
13819             for(i = 0; i < 4; i++)
13820                 if((arg = GET_ARG(i + 1))[0])
13821                 {
13822                     scbonuses[i] = atoi(arg);
13823                 }
13824             break;
13825         case CMD_LEVELORDER_TOTALSCORE:
13826             for(i = 0; i < 10; i++)
13827                 if((arg = GET_ARG(i + 1))[0])
13828                 {
13829                     tscore[i] = atoi(arg);
13830                 }
13831             break;
13832         case CMD_LEVELORDER_MUSICOVERLAP:
13833             CHKDEF;
13834             set->musicoverlap = GET_INT_ARG(1);
13835             break;
13836         case CMD_LEVELORDER_SHOWRUSHBONUS:
13837             showrushbonus = 1;
13838             break;
13839         case CMD_LEVELORDER_NOSLOWFX:
13840             noslowfx = 1;
13841             break;
13842         case CMD_LEVELORDER_EQUALAIRPAUSE:
13843             equalairpause = 1;
13844             break;
13845         case CMD_LEVELORDER_HISCOREBG:
13846             hiscorebg = 1;
13847             break;
13848         case CMD_LEVELORDER_COMPLETEBG:
13849             completebg = 1;
13850             break;
13851         case CMD_LEVELORDER_LOADINGBG:
13852             errormessage = fill_s_loadingbar(&loadingbg[0], GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8));
13853             if(errormessage)
13854             {
13855                 goto lCleanup;
13856             }
13857             break;
13858         case CMD_LEVELORDER_LOADINGBG2:
13859             errormessage = fill_s_loadingbar(&loadingbg[1], GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8));
13860             if(errormessage)
13861             {
13862                 goto lCleanup;
13863             }
13864             break;
13865         case CMD_LEVELORDER_LOADINGMUSIC:
13866             loadingmusic = GET_INT_ARG(1);
13867             break;
13868         case CMD_LEVELORDER_UNLOCKBG:
13869             unlockbg = 1;
13870             break;
13871         case CMD_LEVELORDER_NOSHARE:
13872             noshare = 1;
13873             break;
13874         case CMD_LEVELORDER_CUSTFADE:
13875             //8-2-2005 custom fade
13876             CHKDEF;
13877             set->custfade = GET_INT_ARG(1);
13878             break;
13879         case CMD_LEVELORDER_CONTINUESCORE:
13880             //8-2-2005 custom fade end
13881             //continuescore
13882             CHKDEF;
13883             set->continuescore = GET_INT_ARG(1);
13884             break;
13885         case CMD_LEVELORDER_CREDITS:
13886             CHKDEF;
13887             set->credits = GET_INT_ARG(1);
13888             break;
13889         case CMD_LEVELORDER_TYPEMP:
13890             //typemp for change for mp restored by time (0) to by enemys (1) or no restore (2) by tails
13891             CHKDEF;
13892             set->typemp = GET_INT_ARG(1);
13893             break;
13894         case CMD_LEVELORDER_SINGLE:
13895             if(set)
13896             {
13897                 set->maxplayers = 1;
13898             }
13899             else
13900             {
13901                 defaultmaxplayers = 1;
13902             }
13903             break;
13904         case CMD_LEVELORDER_MAXPLAYERS:
13905             if(set)
13906             {
13907                 set->maxplayers = GET_INT_ARG(1);
13908             }
13909             else
13910             {
13911                 defaultmaxplayers = GET_INT_ARG(1);
13912             }
13913             break;
13914         case CMD_LEVELORDER_NOSAME:
13915             CHKDEF;
13916             set->nosame |= GET_INT_ARG(1) != 0;
13917             set->nosame |= GET_INT_ARG(2) != 0 ? 2 : 0;
13918             break;
13919         case CMD_LEVELORDER_RUSH:
13920             rush[0] = GET_INT_ARG(1);
13921             rush[1] = GET_INT_ARG(2);
13922             strncpy(rush_names[0], GET_ARG(3), MAX_NAME_LEN - 1);
13923             rush[2] = GET_INT_ARG(4);
13924             rush[3] = GET_INT_ARG(5);
13925             strncpy(rush_names[1], GET_ARG(6), MAX_NAME_LEN - 1);
13926             rush[4] = GET_INT_ARG(7);
13927             rush[5] = GET_INT_ARG(8);
13928             break;
13929         case CMD_LEVELORDER_MAXWALLHEIGHT:
13930             MAX_WALL_HEIGHT = GET_INT_ARG(1);
13931             if(MAX_WALL_HEIGHT < 0)
13932             {
13933                 MAX_WALL_HEIGHT = 1000;
13934             }
13935             break;
13936         case CMD_LEVELORDER_SCOREFORMAT:
13937             scoreformat = GET_INT_ARG(1);
13938             break;
13939         case CMD_LEVELORDER_GRAVITY:
13940             default_level_gravity = GET_FLOAT_ARG(1);
13941             default_level_maxfallspeed = GET_FLOAT_ARG(2);
13942             default_level_maxtossspeed = GET_FLOAT_ARG(3);
13943             break;
13944         case CMD_LEVELORDER_SKIPTOSET:
13945             skiptoset = GET_INT_ARG(1);
13946             break;
13947         case CMD_LEVELORDER_NOSHOWCOMPLETE:
13948             CHKDEF;
13949             set->noshowcomplete = GET_INT_ARG(1);
13950             break;
13951         case CMD_LEVELORDER_SPAWNOVERRIDE:
13952             spawnoverride = GET_INT_ARG(1);
13953             break;
13954         case CMD_LEVELORDER_MAXENTITIES:
13955             maxentities = GET_INT_ARG(1);
13956             break;
13957         default:
13958             if (command && command[0])
13959             {
13960                 if (err <= 0) printf("\n");
13961                 ++err;
13962                 printf("Command '%s' not understood in level order!\n", command);
13963             }
13964         }
13965 
13966         // Go to next line
13967         pos += getNewLineStart(buf + pos);
13968     }
13969 
13970 #undef CHKDEF
13971 
13972     // Variables without defaults will be auto populated.
13973     if(olbarstatus.size.x == 0)
13974     {
13975         olbarstatus = lbarstatus;
13976     }
13977 
13978     if(!plifeUsed[0])
13979     {
13980         plife[2][0] = plife[0][0];
13981         plife[2][1] = plife[2][1] + (plife[0][1] - 10);
13982     }
13983     if(!plifeUsed[1])
13984     {
13985         plife[3][0] = plife[1][0];
13986         plife[3][1] = plife[3][1] + (plife[1][1] - 10);
13987     }
13988 
13989     if(!elifeUsed[0])
13990     {
13991         elife[2][0] = elife[0][0];
13992         elife[2][1] = elife[2][1] + (elife[0][1] - 27);
13993     }
13994     if(!elifeUsed[1])
13995     {
13996         elife[3][0] = elife[1][0];
13997         elife[3][1] = elife[3][1] + (elife[1][1] - 27);
13998     }
13999 
14000     if(!piconUsed[0])
14001     {
14002         picon[2][0] = picon[0][0];
14003         picon[2][1] = picon[2][1] + (picon[0][1] - 2);
14004     }
14005     if(!piconUsed[1])
14006     {
14007         picon[3][0] = picon[1][0];
14008         picon[3][1] = picon[3][1] + (picon[1][1] - 2);
14009     }
14010 
14011     if(!piconwUsed[0])
14012     {
14013         piconw[2][0] = piconw[0][0];
14014         piconw[2][1] = piconw[2][1] + (piconw[0][1] - 2);
14015     }
14016     if(!piconwUsed[1])
14017     {
14018         piconw[3][0] = piconw[1][0];
14019         piconw[3][1] = piconw[3][1] + (piconw[1][1] - 2);
14020     }
14021 
14022     if(!eiconUsed[0])
14023     {
14024         eicon[2][0] = eicon[0][0];
14025         eicon[2][1] = eicon[2][1] + (eicon[0][1] - 19);
14026     }
14027     if(!eiconUsed[1])
14028     {
14029         eicon[3][0] = eicon[1][0];
14030         eicon[3][1] = eicon[3][1] + (eicon[1][1] - 19);
14031     }
14032 
14033     if(!pmpUsed[0])
14034     {
14035         pmp[0][0] = plife[0][0];
14036         pmp[0][1] = plife[0][1] + 8;
14037     }
14038     if(!pmpUsed[1])
14039     {
14040         pmp[1][0] = plife[1][0];
14041         pmp[1][1] = plife[1][1] + 8;
14042     }
14043     if(!pmpUsed[2])
14044     {
14045         pmp[2][0] = pmp[0][0];
14046         pmp[2][1] = pmp[2][1] + (pmp[0][1] - 18);
14047     }
14048     if(!pmpUsed[3])
14049     {
14050         pmp[3][0] = pmp[1][0];
14051         pmp[3][1] = pmp[1][1] + (pmp[1][1] - 18);
14052     }
14053 
14054     if(!plifeXused[0])
14055     {
14056         plifeX[0][0] = plife[0][0] + lbarstatus.size.x + 4;
14057         plifeX[0][1] = picon[0][1] + 7;
14058     }
14059     if(!plifeXused[1])
14060     {
14061         plifeX[1][0] = plife[1][0] + lbarstatus.size.x + 4;
14062         plifeX[1][1] = picon[1][1] + 7;
14063     }
14064     if(!plifeXused[2])
14065     {
14066         plifeX[2][0] = plife[2][0] + lbarstatus.size.x + 4;
14067         plifeX[2][1] = picon[2][1] + 7;
14068     }
14069     if(!plifeXused[3])
14070     {
14071         plifeX[3][0] = plife[3][0] + lbarstatus.size.x + 4;
14072         plifeX[3][1] = picon[3][1] + 7;
14073     }
14074     for(i = 0; i < MAX_PLAYERS; i++) if(plifeX[i][2] == -1)
14075         {
14076             plifeX[i][2] = 0;
14077         }
14078 
14079     if(!plifeNused[0])
14080     {
14081         plifeN[0][0] = plife[0][0] + lbarstatus.size.x + 11;
14082         plifeN[0][1] = picon[0][1];
14083     }
14084     if(!plifeNused[1])
14085     {
14086         plifeN[1][0] = plife[1][0] + lbarstatus.size.x + 11;
14087         plifeN[1][1] = picon[1][1];
14088     }
14089     if(!plifeNused[2])
14090     {
14091         plifeN[2][0] = plifeN[0][0];
14092         plifeN[2][1] = picon[2][1];
14093     }
14094     if(!plifeNused[3])
14095     {
14096         plifeN[3][0] = plifeN[1][0];
14097         plifeN[3][1] = picon[3][1];
14098     }
14099     for(i = 0; i < MAX_PLAYERS; i++) if(plifeN[i][2] == -1)
14100         {
14101             plifeN[i][2] = 3;
14102         }
14103 
14104     if(!pnameJused[0])
14105     {
14106         pnameJ[0][2] = pnameJ[0][4] = pnameJ[0][0] = plife[0][0] + 1;
14107         pnameJ[0][5] = pnameJ[0][1] = picon[0][1];
14108         pnameJ[0][3] = 10 + pnameJ[0][5];
14109     }
14110     if(!pnameJused[1])
14111     {
14112         pnameJ[1][2] = pnameJ[1][4] = pnameJ[1][0] = plife[1][0] + 1;
14113         pnameJ[1][5] = pnameJ[1][1] = picon[1][1];
14114         pnameJ[1][3] = 10 + pnameJ[1][5];
14115     }
14116     if(!pnameJused[2])
14117     {
14118         pnameJ[2][2] = pnameJ[2][4] = pnameJ[2][0] = plife[2][0] + 1;
14119         pnameJ[2][5] = pnameJ[2][1] = picon[2][1];
14120         pnameJ[2][3] = 10 + pnameJ[2][5];
14121     }
14122     if(!pnameJused[3])
14123     {
14124         pnameJ[3][2] = pnameJ[3][4] = pnameJ[3][0] = plife[3][0] + 1;
14125         pnameJ[3][5] = pnameJ[3][1] = picon[3][1];
14126         pnameJ[3][3] = 10 + pnameJ[3][5];
14127     }
14128     for(i = 0; i < MAX_PLAYERS; i++) if(pnameJ[i][6] == -1)
14129         {
14130             pnameJ[i][6] = 0;
14131         }
14132 
14133     if(!pscoreUsed[0])
14134     {
14135         pscore[0][0] = plife[0][0] + 1;
14136         pscore[0][1] = picon[0][1];
14137     }
14138     if(!pscoreUsed[1])
14139     {
14140         pscore[1][0] = plife[1][0] + 1;
14141         pscore[1][1] = picon[1][1];
14142     }
14143     if(!pscoreUsed[2])
14144     {
14145         pscore[2][0] = plife[2][0] + 1;
14146         pscore[2][1] = picon[2][1];
14147     }
14148     if(!pscoreUsed[3])
14149     {
14150         pscore[3][0] = plife[3][0] + 1;
14151         pscore[3][1] = picon[3][1];
14152     }
14153     for(i = 0; i < MAX_PLAYERS; i++) if(pscore[i][6] == -1)
14154         {
14155             pscore[i][6] = 0;
14156         }
14157 
14158     if(!enameused[0])
14159     {
14160         ename[0][0] = elife[0][0] + 1;
14161         ename[0][1] = eicon[0][1];
14162     }
14163     if(!enameused[1])
14164     {
14165         ename[1][0] = elife[1][0] + 1;
14166         ename[1][1] = eicon[1][1];
14167     }
14168     if(!enameused[2])
14169     {
14170         ename[2][0] = ename[0][0];
14171         ename[2][1] = eicon[2][1];
14172     }
14173     if(!enameused[3])
14174     {
14175         ename[3][0] = ename[1][0];
14176         ename[3][1] = eicon[3][1];
14177     }
14178     for(i = 0; i < MAX_PLAYERS; i++) if(ename[i][2] == -1)
14179         {
14180             ename[i][2] = 0;
14181         }
14182 
14183     branch_name[0] = 0; //clear up branch name, so we can use it in game
14184 
14185     for(i = 0; i < MAX_PLAYERS; i++) if(pshoot[i][2] == -1)
14186         {
14187             pshoot[i][2] = 2;
14188         }
14189     if(timeloc[5] == -1)
14190     {
14191         timeloc[5] = 3;
14192     }
14193 
14194     if(!set)
14195     {
14196         errormessage = "No levels were loaded!";
14197     }
14198 
14199     //assume old mods have same maxplayers for all sets
14200     else if(!psmenu[0][0] && !psmenu[0][1])
14201     {
14202         for(i = 0; i < set->maxplayers; i++)
14203         {
14204             psmenu[i][0] = (set->maxplayers > 2) ? ((111 - (set->maxplayers * 14)) + ((i * (320 - (166 / set->maxplayers)) / set->maxplayers) + videomodes.hShift)) :
14205                            (83 + (videomodes.hShift / 2) + (i * (155 + videomodes.hShift)));
14206             psmenu[i][1] = 230 + videomodes.vShift;
14207             psmenu[i][2] = (set->maxplayers > 2) ? ((95 - (set->maxplayers * 14)) + ((i * (320 - (166 / set->maxplayers)) / set->maxplayers) + videomodes.hShift)) :
14208                            (67 + (videomodes.hShift / 2) + (i * (155 + videomodes.hShift)));
14209             psmenu[i][3] = 225 + videomodes.vShift;
14210         }
14211     }
14212 
14213 lCleanup:
14214 
14215     if(buf)
14216     {
14217         free(buf);
14218     }
14219 
14220     if(!savelevel)
14221     {
14222         savelevel = calloc(num_difficulties, sizeof(*savelevel));
14223     }
14224 
14225     if(errormessage)
14226     {
14227         borShutdown(1, "load_levelorder ERROR in %s at %d, msg: %s\n", filename, line, errormessage);
14228     }
14229 }
14230 
14231 
free_level(s_level * lv)14232 void free_level(s_level *lv)
14233 {
14234     int i;
14235 
14236     if(!lv)
14237     {
14238         return;
14239     }
14240 
14241     //offload layers
14242     for(i = 1; i < lv->numlayers; i++)
14243     {
14244         if(lv->layers[i].gfx.handle && lv->layers[i].gfx.handle != background)
14245         {
14246             if(lv->layers[i].gfx.sprite->magic == sprite_magic &&
14247                lv->layers[i].gfx.sprite->mask != NULL)
14248             {
14249                 free(lv->layers[i].gfx.sprite->mask);
14250             }
14251             free(lv->layers[i].gfx.handle);
14252             lv->layers[i].gfx.handle = NULL;
14253         }
14254     }
14255 
14256     //offload textobjs
14257     for(i = 0; i < lv->numtextobjs; i++)
14258     {
14259         if(lv->textobjs[i].text)
14260         {
14261             free(lv->textobjs[i].text);
14262             lv->textobjs[i].text = NULL;
14263         }
14264     }
14265 
14266     //offload basemaps
14267     for(i = 0; i < lv->numbasemaps; i++)
14268     {
14269         if(lv->basemaps[i].map)
14270         {
14271             free(lv->basemaps[i].map);
14272         }
14273     }
14274 
14275     //offload scripts
14276     Script_Clear(&(lv->update_script), 2);
14277     Script_Clear(&(lv->updated_script), 2);
14278     Script_Clear(&(lv->key_script), 2);
14279     Script_Clear(&(lv->level_script), 2);
14280     Script_Clear(&(lv->endlevel_script), 2);
14281 
14282     for(i = 0; i < lv->numspawns; i++)
14283     {
14284         Script_Clear(&(lv->spawnpoints[i].spawnscript), 2);
14285     }
14286 
14287     if(lv->spawnpoints)
14288     {
14289         free(lv->spawnpoints);
14290     }
14291     if(lv->layers)
14292     {
14293         free(lv->layers);
14294     }
14295     if(lv->layersref)
14296     {
14297         free(lv->layersref);
14298     }
14299     if(lv->panels)
14300     {
14301         free(lv->panels);
14302     }
14303     if(lv->frontpanels)
14304     {
14305         free(lv->frontpanels);
14306     }
14307     if(lv->bglayers)
14308     {
14309         free(lv->bglayers);
14310     }
14311     if(lv->fglayers)
14312     {
14313         free(lv->fglayers);
14314     }
14315     if(lv->genericlayers)
14316     {
14317         free(lv->genericlayers);
14318     }
14319     if(lv->waters)
14320     {
14321         free(lv->waters);
14322     }
14323     if(lv->textobjs)
14324     {
14325         free(lv->textobjs);
14326     }
14327     if(lv->holes)
14328     {
14329         free(lv->holes);
14330     }
14331     if(lv->spawn)
14332     {
14333         free(lv->spawn);
14334     }
14335     if(lv->walls)
14336     {
14337         free(lv->walls);
14338     }
14339     if(lv->basemaps)
14340     {
14341         free(lv->basemaps);
14342     }
14343     if(lv->palettes)
14344     {
14345         free(lv->palettes);
14346     }
14347 
14348     free(lv);
14349     lv = NULL;
14350 }
14351 
14352 
unload_level()14353 void unload_level()
14354 {
14355     s_model *temp;
14356     int i;
14357 
14358     kill_all();
14359     unload_background();
14360 
14361     if(level)
14362     {
14363 
14364         level->pos = 0;
14365         level->advancetime = 0;
14366         level->quake = 0;
14367         level->quaketime = 0;
14368         level->waiting = 0;
14369 
14370         printf("Level Unloading: '%s'\n", level->name);
14371         getRamStatus(BYTES);
14372         free(level->name);
14373         level->name = NULL;
14374         free_level(level);
14375         level = NULL;
14376         temp = getFirstModel();
14377         do
14378         {
14379             if(!temp)
14380             {
14381                 break;
14382             }
14383             if((temp->unload & 2))
14384             {
14385                 cache_model_sprites(temp, 0);
14386             }
14387             if((temp->unload & 1))
14388             {
14389                 free_model(temp);
14390                 temp = getCurrentModel();
14391             }
14392             else
14393             {
14394                 temp = getNextModel();
14395             }
14396         }
14397         while(temp);
14398         printf("RAM Status:\n");
14399         getRamStatus(BYTES);
14400 
14401 
14402     }
14403 
14404     advancex = 0;
14405     advancey = 0;
14406     nojoin = 0;
14407     is_total_timeover = 0;
14408     current_spawn = 0;
14409     groupmin = 100;
14410     groupmax = 100;
14411     scrollminz = 0;
14412     scrollmaxz = 0;
14413     scrollminx = 0;
14414     scrollmaxx = 0;
14415     blockade = 0;
14416     level_completed = 0;
14417     level_completed_defeating_boss = 0;
14418     tospeedup = 0;    // Reset so it sets to normal speed for the next level
14419     for (i = 0; i < MAX_PLAYERS; i++) reached[i] = 0; // TYPE_ENDLEVEL values reset after level completed //4player
14420     showtimeover = 0;
14421     _pause = 0;
14422     endgame = 0;
14423     go_time = 0;
14424     debug_time = 0;
14425     debug_xy_msg.x = -1;
14426     debug_xy_msg.y = -1;
14427     neon_time = 0;
14428     _time = 0;
14429     cameratype = 0;
14430     light.x = 128;
14431     light.y = 64;
14432     gfx_y_offset = gfx_x_offset = gfx_y_offset_adj = 0;    // Added so select screen graphics display correctly
14433 }
14434 
14435 
addhole(float x,float z,float x1,float x2,float x3,float x4,float depth,float alt,int type)14436 static void addhole(float x, float z, float x1, float x2, float x3, float x4, float depth, float alt, int type)
14437 {
14438     __realloc(level->holes, level->numholes);
14439     level->holes[level->numholes].x = x;
14440     level->holes[level->numholes].z = z;
14441     level->holes[level->numholes].upperleft = x1;
14442     level->holes[level->numholes].lowerleft = x2;
14443     level->holes[level->numholes].upperright = x3;
14444     level->holes[level->numholes].lowerright = x4;
14445     level->holes[level->numholes].depth = depth;
14446     level->holes[level->numholes].height = alt;
14447     level->holes[level->numholes].type = type;
14448 
14449     level->numholes++;
14450 }
14451 
addwall(float x,float z,float x1,float x2,float x3,float x4,float depth,float alt,int type)14452 static void addwall(float x, float z, float x1, float x2, float x3, float x4, float depth, float alt, int type)
14453 {
14454     __realloc(level->walls, level->numwalls);
14455     level->walls[level->numwalls].x = x;
14456     level->walls[level->numwalls].z = z;
14457     level->walls[level->numwalls].upperleft = x1;
14458     level->walls[level->numwalls].lowerleft = x2;
14459     level->walls[level->numwalls].upperright = x3;
14460     level->walls[level->numwalls].lowerright = x4;
14461     level->walls[level->numwalls].depth = depth;
14462     level->walls[level->numwalls].height = alt;
14463     level->walls[level->numwalls].type = type;
14464 
14465     level->numwalls++;
14466 }
14467 
addbasemap(float rx,float rz,float x_size,float z_size,float min_y,float max_y,int x_cont)14468 static void addbasemap(float rx, float rz, float x_size, float z_size, float min_y, float max_y, int x_cont)
14469 {
14470     __realloc(level->basemaps, level->numbasemaps);
14471     level->numbasemaps++;
14472     generate_basemap(level->numbasemaps-1, rx, rz, x_size, z_size, min_y, max_y, x_cont);
14473 }
14474 
generate_basemap(int map_index,float rx,float rz,float x_size,float z_size,float min_y,float max_y,int x_cont)14475 void generate_basemap(int map_index, float rx, float rz, float x_size, float z_size, float min_y, float max_y, int x_cont) {
14476     float x, z;
14477 	float delta, y, tmp;
14478 	int dir = 0;
14479 
14480     if(map_index >= level->numbasemaps)
14481     {
14482         __reallocto(level->basemaps, level->numbasemaps, map_index + 1);
14483         level->numbasemaps = map_index + 1;
14484     }
14485 
14486     level->basemaps[map_index].position.x = rx;
14487     level->basemaps[map_index].position.z = rz-z_size;
14488     level->basemaps[map_index].size.x = x_size;
14489     level->basemaps[map_index].size.z = z_size;
14490 
14491     if(!level->basemaps[map_index].map)
14492     {
14493         level->basemaps[map_index].map = calloc( 1, (int)(sizeof(*(level->basemaps[map_index].map)) * (x_size+1)*(z_size+1)) );
14494     }
14495 
14496     if (min_y <= max_y) dir = 1;
14497     else
14498     {
14499         dir = 0;
14500         tmp = min_y;
14501         min_y = max_y;
14502         max_y = tmp;
14503     }
14504 
14505 	delta = (max_y - min_y) / ( (x_size <= 0) ? 1 : (x_size-1) );
14506 
14507 	for( x = 0; x < x_size; x++)
14508     {
14509 		if ( dir )
14510         {
14511             if ( x == x_size-1 ) y = max_y;
14512             else y = x*delta + min_y;
14513         }
14514 		else
14515         {
14516             y = max_y - (x*delta);
14517         }
14518 
14519 		if ( x_cont != 0 )
14520         {
14521             if ( dir > 0 )
14522             {
14523                 if ( x+rx >= x_cont ) y = max_y; // connect with the wall more smoothly
14524             }
14525             else
14526             {
14527                 if ( x+rx <= x_cont ) y = max_y;
14528             }
14529 		}
14530 
14531 		for ( z = 0; z < z_size; z++)
14532         {
14533 			level->basemaps[map_index].map[(int)(x + z*x_size)] = y;
14534 			//printf("map[%d] = %f\n",(int)(x + z*x_size),y);
14535 		}
14536 		//printf("x:%f y:%f delta:%f\n",x,y,delta);
14537 		//printf("y: %f\n",y);
14538 	}
14539 
14540 	return;
14541 }
14542 
load_level(char * filename)14543 void load_level(char *filename)
14544 {
14545     char *buf = NULL;
14546     size_t size, len, sblen;
14547     ptrdiff_t pos, oldpos;
14548     char *command;
14549     char *value;
14550     char *scriptbuf = NULL;
14551     char string[MAX_BUFFER_LEN] = {""};
14552     s_spawn_entry next;
14553     s_model *tempmodel, *cached_model;
14554 
14555     int i = 0, j = 0, crlf = 0;
14556     int player_max = MAX_PLAYERS;
14557     char bgPath[MAX_BUFFER_LEN] = {""}, fnbuf[MAX_BUFFER_LEN];
14558     s_loadingbar bgPosi = {0, 0, {0,0}, {0,0}, 0, 0};
14559     char musicPath[MAX_BUFFER_LEN] = {""};
14560     u32 musicOffset = 0;
14561 
14562     ArgList arglist;
14563     char argbuf[MAX_ARG_LEN + 1] = "";
14564 
14565     ArgList arglist2;
14566     char argbuf2[MAX_ARG_LEN + 1] = "";
14567 
14568     levelCommands cmd;
14569     levelCommands cmd2;
14570     int line = 0;
14571     char *errormessage = NULL;
14572     char *scriptname = NULL;
14573     Script *tempscript = NULL;
14574     s_drawmethod *dm;
14575     s_layer *bgl;
14576     int (*panels)[3] = NULL;
14577     int *order = NULL;
14578     int panelcount = 0;
14579     int exit_blocked = 0, exit_hole = 0;
14580     char maskPath[MAX_BUFFER_LEN] = {""};
14581 
14582     unload_level();
14583 
14584     printf("Level Loading:   '%s'\n", filename);
14585 
14586 
14587 
14588     getRamStatus(BYTES);
14589 
14590     if(isLoadingScreenTypeBg(loadingbg[1].set))
14591     {
14592         if(custBkgrds)
14593         {
14594             strcpy(string, custBkgrds);
14595             strcat(string, "loading2");
14596             load_background(string);
14597         }
14598         else
14599         {
14600             load_cached_background("data/bgs/loading2");
14601         }
14602         clearscreen(vscreen);
14603         spriteq_clear();
14604         standard_palette(1);
14605     }
14606 
14607     if(isLoadingScreenTypeBar(loadingbg[1].set))
14608     {
14609         lifebar_colors();
14610         init_colourtable();
14611     }
14612 
14613     update_loading(&loadingbg[1], -1, 1); // initialize the update screen
14614 
14615     memset(&next, 0, sizeof(next));
14616 
14617     level = calloc(1, sizeof(*level));
14618     if(!level)
14619     {
14620         errormessage = "load_level() #1 FATAL: Out of memory!";
14621         goto lCleanup;
14622     }
14623     len = strlen(filename);
14624     level->name = malloc(len + 1);
14625 
14626     if(!level->name)
14627     {
14628         errormessage = "load_level() #1 FATAL: Out of memory!";
14629         goto lCleanup;
14630     }
14631     strcpy(level->name, filename);
14632 
14633     // Allocate memory for player spawn - only as much as we need.
14634     player_max = levelsets[current_set].maxplayers;
14635     level->spawn = calloc(player_max, sizeof(*level->spawn));
14636 
14637     // Default player spawn Y position just above the screen top.
14638     for(i = 0; i < player_max && level->spawn; i++)
14639     {
14640         level->spawn[i].y = videomodes.vRes + 60;
14641     }
14642 
14643     if(buffer_pakfile(filename, &buf, &size) != 1)
14644     {
14645         errormessage = "Unable to load level file!";
14646         goto lCleanup;
14647     }
14648 
14649     level->settime          = 100;                          // Feb 25, 2005 - Default time limit set to 100
14650     level->nospecial        = 0;                            // Default set to specials can be used during bonus levels
14651     level->nohurt           = DAMAGE_FROM_ENEMY_ON;
14652     level->nohit            = DAMAGE_FROM_PLAYER_ON;        // Default able to hit the other player
14653     level->setweap          = 0;
14654     level->maxtossspeed     = default_level_maxtossspeed;
14655     level->maxfallspeed     = default_level_maxfallspeed;
14656     level->gravity          = default_level_gravity;
14657     level->scrolldir        = SCROLL_RIGHT;
14658     level->scrollspeed      = 1;
14659     level->cameraxoffset    = 0;
14660     level->camerazoffset    = 0;
14661     level->boss_slow        = BOSS_SLOW_ON;
14662     level->bossescount      = 0;
14663     level->numbosses        = 0;
14664     blendfx[BLEND_MULTIPLY] = 1;
14665     bgtravelled             = 0;
14666     vbgtravelled            = 0;
14667     traveltime              = 0;
14668     texttime                = 0;
14669     nopause                 = 0;
14670     nofadeout               = 0;
14671     noscreenshot            = 0;
14672 
14673     panel_width = panel_height = frontpanels_loaded = 0;
14674 
14675     //reset_playable_list(1);
14676 
14677     // Now interpret the contents of buf line by line
14678     pos = 0;
14679     while(pos < size)
14680     {
14681         line++;
14682         ParseArgs(&arglist, buf + pos, argbuf);
14683         command = GET_ARG(0);
14684         cmd = getLevelCommand(levelcmdlist, command);
14685         switch(cmd)
14686         {
14687         case CMD_LEVEL_LOADINGBG:
14688             load_background(GET_ARG(1));
14689             errormessage = fill_s_loadingbar(&bgPosi, GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8), GET_INT_ARG(9));
14690             if (errormessage)
14691             {
14692                 goto lCleanup;
14693             }
14694             standard_palette(1);
14695             lifebar_colors();
14696             init_colourtable();
14697             update_loading(&bgPosi, -1, 1); // initialize the update screen
14698             break;
14699         case CMD_LEVEL_MUSICFADE:
14700             memset(&next, 0, sizeof(next));
14701             next.musicfade = GET_FLOAT_ARG(1);
14702             break;
14703         case CMD_LEVEL_MUSIC:
14704             value = GET_ARG(1);
14705             strncpy(string, value, MAX_BUFFER_LEN - 1);
14706             musicOffset = atol(GET_ARG(2));
14707             if(loadingmusic)
14708             {
14709                 music(string, 1, musicOffset);
14710                 musicPath[0] = 0;
14711             }
14712             else
14713             {
14714                 oldpos = pos;
14715                 // Go to next line
14716                 pos += getNewLineStart(buf + pos);
14717 #define GET_ARG2(z) arglist2.count > z ? arglist2.args[z] : ""
14718                 if(pos < size)
14719                 {
14720                     ParseArgs(&arglist2, buf + pos, argbuf2);
14721                     command = GET_ARG2(0);
14722                     cmd2 = getLevelCommand(levelcmdlist, command);
14723                 }
14724                 else
14725                 {
14726                     cmd2 = (levelCommands) 0;
14727                 }
14728 
14729                 if(cmd2 == CMD_LEVEL_AT)
14730                 {
14731                     if(next.musicfade == 0)
14732                     {
14733                         memset(&next, 0, sizeof(next));
14734                     }
14735                     strncpy(next.music, string, MAX_BUFFER_LEN);
14736                     next.musicoffset = musicOffset;
14737                 }
14738                 else
14739                 {
14740                     strncpy(musicPath, string, MAX_BUFFER_LEN);
14741                 }
14742                 pos = oldpos;
14743 #undef GET_ARG2
14744             }
14745             break;
14746         case CMD_LEVEL_ALLOWSELECT:
14747             load_playable_list(buf + pos);
14748             break;
14749         case CMD_LEVEL_LOAD:
14750 #ifdef DEBUG
14751             printf("load_level: load %s, %s\n", GET_ARG(1), filename);
14752 #endif
14753             tempmodel = findmodel(GET_ARG(1));
14754             if (!tempmodel)
14755             {
14756                 load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2));
14757             }
14758             else
14759             {
14760                 update_model_loadflag(tempmodel, GET_INT_ARG(2));
14761             }
14762             break;
14763         case CMD_LEVEL_ALPHAMASK:
14764             strncpy(maskPath, GET_ARG(1), MAX_BUFFER_LEN - 1);
14765             break;
14766         case CMD_LEVEL_BACKGROUND:
14767         case CMD_LEVEL_BGLAYER:
14768         case CMD_LEVEL_LAYER:
14769         case CMD_LEVEL_FGLAYER:
14770             __realloc(level->layers, level->numlayers);
14771             bgl = &(level->layers[level->numlayers]);
14772 
14773             if(cmd == CMD_LEVEL_BACKGROUND || cmd == CMD_LEVEL_BGLAYER)
14774             {
14775                 i = 0;
14776                 bgl->z = MIN_INT;
14777             }
14778             else
14779             {
14780                 i = 1;
14781                 bgl->z =  GET_FLOAT_ARG(2);
14782                 if(cmd == CMD_LEVEL_FGLAYER)
14783                 {
14784                     bgl->z += FRONTPANEL_Z;
14785                 }
14786             }
14787 
14788             if(cmd == CMD_LEVEL_BACKGROUND)
14789             {
14790                 if(bgPath[0])
14791                 {
14792                     errormessage = "Background is already defined!";
14793                     goto lCleanup;
14794                 }
14795                 value = GET_ARG(1);
14796                 strcpy(bgPath, value);
14797                 bgl->oldtype = BGT_BACKGROUND;
14798             }
14799             else if(cmd == CMD_LEVEL_BGLAYER)
14800             {
14801                 bgl->oldtype = BGT_BGLAYER;
14802             }
14803             else if(cmd == CMD_LEVEL_FGLAYER)
14804             {
14805                 bgl->oldtype = BGT_FGLAYER;
14806             }
14807             else if(cmd == CMD_LEVEL_LAYER)
14808             {
14809                 bgl->oldtype = BGT_GENERIC;
14810             }
14811 
14812             dm = &(bgl->drawmethod);
14813             *dm = plainmethod;
14814 
14815             bgl->ratio.x = GET_FLOAT_ARG(i + 2); // x ratio
14816             bgl->ratio.z = GET_FLOAT_ARG(i + 3); // z ratio
14817             bgl->offset.x = GET_INT_ARG(i + 4); // x start
14818             bgl->offset.z = GET_INT_ARG(i + 5); // z start
14819             bgl->spacing.x = GET_INT_ARG(i + 6); // x spacing
14820             bgl->spacing.z = GET_INT_ARG(i + 7); // z spacing
14821             dm->xrepeat = GET_INT_ARG(i + 8); // x repeat
14822             dm->yrepeat = GET_INT_ARG(i + 9); // z repeat
14823             dm->transbg = GET_INT_ARG(i + 10); // transparency
14824             dm->alpha = GET_INT_ARG(i + 11); // alpha
14825             dm->water.watermode = GET_INT_ARG(i + 12); // water
14826             if(dm->water.watermode == WATER_MODE_SHEAR)
14827             {
14828                 dm->water.beginsize = GET_FLOAT_ARG(i + 13); // beginsize
14829                 dm->water.endsize = GET_FLOAT_ARG(i + 14); // endsize
14830                 dm->water.perspective = GET_INT_ARG(i + 15); // perspective
14831             }
14832             else
14833             {
14834                 dm->water.amplitude = GET_INT_ARG(i + 13); // amplitude
14835                 dm->water.wavelength = GET_FLOAT_ARG(i + 14); // wavelength
14836                 dm->water.wavespeed = GET_FLOAT_ARG(i + 15); // waterspeed
14837             }
14838             bgl->bgspeedratio = GET_FLOAT_ARG(i + 16); // moving
14839             bgl->quake = GET_INT_ARG(i + 17); // quake
14840             bgl->neon = GET_INT_ARG(i + 18); // neon
14841             bgl->enabled = 1; // enabled
14842 
14843             if((GET_ARG(i + 2))[0] == 0)
14844             {
14845                 bgl->ratio.x = (cmd == CMD_LEVEL_FGLAYER ? 1.5 : 0.5);
14846             }
14847             if((GET_ARG(i + 3))[0] == 0)
14848             {
14849                 bgl->ratio.z = (cmd == CMD_LEVEL_FGLAYER ? 1.5 : 0.5);
14850             }
14851 
14852             if((GET_ARG(i + 8))[0] == 0)
14853             {
14854                 dm->xrepeat = -1;
14855             }
14856             if((GET_ARG(i + 9))[0] == 0)
14857             {
14858                 dm->yrepeat = -1;
14859             }
14860             if(cmd == CMD_LEVEL_BACKGROUND && (GET_ARG(i + 16))[0] == 0)
14861             {
14862                 bgl->bgspeedratio = 1.0;
14863             }
14864 
14865             if(blendfx_is_set == 0 && dm->alpha)
14866             {
14867                 blendfx[dm->alpha - 1] = 1;
14868             }
14869 
14870             if(cmd != CMD_LEVEL_BACKGROUND)
14871             {
14872                 load_layer(GET_ARG(1), maskPath, level->numlayers);
14873             }
14874             level->numlayers++;
14875             break;
14876         case CMD_LEVEL_WATER:
14877             __realloc(level->layers, level->numlayers);
14878             bgl = &(level->layers[level->numlayers]);
14879             dm = &(bgl->drawmethod);
14880             *dm = plainmethod;
14881 
14882             bgl->oldtype = BGT_WATER;
14883             bgl->z = MIN_INT + 1;
14884 
14885             bgl->ratio.x = 0.5; // x ratio
14886             bgl->ratio.z = 0.5; // z ratio
14887             bgl->offset.x = 0; // x start
14888             bgl->offset.z = NaN; // z start
14889             bgl->spacing.x = 0; // x spacing
14890             bgl->spacing.z = 0; // z spacing
14891             dm->xrepeat = -1; // x repeat
14892             dm->yrepeat = 1; // z repeat
14893             dm->transbg = 0; // transparency
14894             dm->alpha = BLEND_MODE_NONE; // alpha
14895             dm->water.watermode = WATER_MODE_SINE;
14896             dm->water.amplitude = GET_INT_ARG(2); // amplitude
14897             dm->water.wavelength = 40; // wavelength
14898             dm->water.wavespeed = 1.0; // waterspeed
14899             bgl->bgspeedratio = 0; // moving
14900             bgl->enabled = 1; // enabled
14901 
14902             if(dm->water.amplitude < 1)
14903             {
14904                 dm->water.amplitude = 1;
14905             }
14906 
14907             load_layer(GET_ARG(1), maskPath, level->numlayers);
14908             level->numlayers++;
14909             break;
14910         case CMD_LEVEL_DIRECTION:
14911             value = GET_ARG(1);
14912             if(stricmp(value, "up") == 0)
14913             {
14914                 level->scrolldir = SCROLL_UP;
14915             }
14916             else if(stricmp(value, "down") == 0)
14917             {
14918                 level->scrolldir = SCROLL_DOWN;
14919             }
14920             else if(stricmp(value, "left") == 0)
14921             {
14922                 level->scrolldir = SCROLL_LEFT;
14923             }
14924             else if(stricmp(value, "both") == 0 || stricmp(value, "rightleft") == 0)
14925             {
14926                 level->scrolldir = SCROLL_BOTH;
14927             }
14928             else if(stricmp(value, "leftright") == 0)
14929             {
14930                 level->scrolldir = SCROLL_LEFTRIGHT;
14931             }
14932             else if(stricmp(value, "right") == 0)
14933             {
14934                 level->scrolldir = SCROLL_RIGHT;
14935             }
14936             else if(stricmp(value, "in") == 0)
14937             {
14938                 level->scrolldir = SCROLL_INWARD;
14939             }
14940             else if(stricmp(value, "out") == 0)
14941             {
14942                 level->scrolldir = SCROLL_OUTWARD;
14943             }
14944             else if(stricmp(value, "inout") == 0)
14945             {
14946                 level->scrolldir = SCROLL_INOUT;
14947             }
14948             else if(stricmp(value, "outin") == 0)
14949             {
14950                 level->scrolldir = SCROLL_OUTIN;
14951             }
14952             break;
14953         case CMD_LEVEL_FACING:
14954             level->facing = GET_INT_ARG(1);
14955             break;
14956         case CMD_LEVEL_ROCK:
14957             level->rocking = GET_INT_ARG(1);
14958             break;
14959         case CMD_LEVEL_BGSPEED:
14960             level->bgspeed = GET_FLOAT_ARG(1);
14961             if(GET_INT_ARG(2))
14962             {
14963                 level->bgspeed *= -1;
14964             }
14965             break;
14966         case CMD_LEVEL_VBGSPEED:
14967             level->vbgspeed = GET_FLOAT_ARG(1);
14968             if(GET_INT_ARG(2))
14969             {
14970                 level->vbgspeed *= -1;
14971             }
14972             break;
14973         case CMD_LEVEL_SCROLLSPEED:
14974             level->scrollspeed = GET_FLOAT_ARG(1);
14975             break;
14976         case CMD_LEVEL_MIRROR:
14977             level->mirror = GET_INT_ARG(1);
14978             break;
14979         case CMD_LEVEL_BOSSMUSIC:
14980             strncpy(level->bossmusic, GET_ARG(1), 255);
14981             level->bossmusic_offset = atol(GET_ARG(2));
14982             break;
14983         case CMD_LEVEL_NOSAVE:
14984             nosave = GET_INT_ARG(1);
14985             break;
14986         case CMD_LEVEL_NOFADEOUT:
14987             nofadeout = GET_INT_ARG(1);
14988             break;
14989         case CMD_LEVEL_NOPAUSE:
14990             nopause = GET_INT_ARG(1);
14991             break;
14992         case CMD_LEVEL_NOSCREENSHOT:
14993             noscreenshot = GET_INT_ARG(1);
14994             break;
14995         case CMD_LEVEL_SETTIME:
14996             // If settime is found, overwrite the default 100 for time limit
14997             level->settime = GET_INT_ARG(1);
14998             if(level->settime > 100 || level->settime < 0)
14999             {
15000                 level->settime = 100;
15001             }
15002             // Feb 25, 2005 - time limit loaded from individual .txt file
15003             break;
15004         case CMD_LEVEL_SETWEAP:
15005             // Specify a weapon for each level
15006             level->setweap = GET_INT_ARG(1);
15007             break;
15008         case CMD_LEVEL_NOTIME:
15009             // Flag to if the time should be displayed 1 = no, else yes
15010             level->notime = GET_INT_ARG(1);
15011             break;
15012         case CMD_LEVEL_NORESET:
15013             // Flag to if the time should be reset when players respawn 1 = no, else yes
15014             level->noreset = GET_INT_ARG(1);
15015             break;
15016         case CMD_LEVEL_NOSLOW:
15017 
15018             if(GET_INT_ARG(1))
15019             {
15020                 level->boss_slow = BOSS_SLOW_OFF;
15021             }
15022 
15023             break;
15024         case CMD_LEVEL_TYPE:
15025             level->type = GET_INT_ARG(1);    // Level type - 1 = bonus, else regular
15026             level->nospecial = GET_INT_ARG(2);    // Can use specials during bonus levels (default 0 - yes)
15027 
15028             if(GET_INT_ARG(3))
15029             {
15030                 level->nohurt = DAMAGE_FROM_ENEMY_OFF;
15031             }
15032 
15033             break;
15034         case CMD_LEVEL_NOHIT:
15035 
15036             if(GET_INT_ARG(1))
15037             {
15038                 level->nohit = DAMAGE_FROM_PLAYER_OFF;
15039             }
15040 
15041             break;
15042         case CMD_LEVEL_GRAVITY:
15043             level->gravity = GET_FLOAT_ARG(1);
15044             level->gravity /= 100;
15045             break;
15046         case CMD_LEVEL_MAXFALLSPEED:
15047             level->maxfallspeed = GET_FLOAT_ARG(1);
15048             level->maxfallspeed /= 10;
15049             break;
15050         case CMD_LEVEL_MAXTOSSSPEED:
15051             level->maxtossspeed = GET_FLOAT_ARG(1);
15052             level->maxtossspeed /= 10;
15053             break;
15054         case CMD_LEVEL_CAMERATYPE:
15055             cameratype = GET_INT_ARG(1);
15056             break;
15057         case CMD_LEVEL_CAMERAOFFSET:
15058             level->cameraxoffset = GET_INT_ARG(1);
15059             level->camerazoffset = GET_INT_ARG(2);
15060             break;
15061         case CMD_LEVEL_SPAWN1:
15062         case CMD_LEVEL_SPAWN2:
15063         case CMD_LEVEL_SPAWN3:
15064         case CMD_LEVEL_SPAWN4:
15065             switch(cmd)
15066             {
15067             case CMD_LEVEL_SPAWN1:
15068                 i = 0;
15069                 break;
15070             case CMD_LEVEL_SPAWN2:
15071                 i = 1;
15072                 break;
15073             case CMD_LEVEL_SPAWN3:
15074                 i = 2;
15075                 break;
15076             case CMD_LEVEL_SPAWN4:
15077                 i = 3;
15078                 break;
15079             default:
15080                 assert(0);
15081             }
15082 
15083             // Verify specified player index exists,
15084             // then set values.
15085             if(level->spawn && i < player_max)
15086             {
15087                 level->spawn[i].x = GET_INT_ARG(1);
15088                 level->spawn[i].z = GET_INT_ARG(2);
15089                 level->spawn[i].y = GET_INT_ARG(3);
15090 
15091                 if(level->spawn[i].y < 0) level->spawn[i].y = videomodes.vRes + 60;
15092             }
15093 
15094             break;
15095         case CMD_LEVEL_FRONTPANEL:
15096         case CMD_LEVEL_PANEL:
15097             if(level->numlayers == 0)
15098             {
15099                 __realloc(level->layers, level->numlayers);
15100                 level->numlayers = 1; // reserve for background
15101             }
15102 
15103             __realloc(level->layers, level->numlayers);
15104             bgl = &(level->layers[level->numlayers]);
15105             dm = &(bgl->drawmethod);
15106             *dm = plainmethod;
15107 
15108             bgl->oldtype = (cmd == CMD_LEVEL_FRONTPANEL ? BGT_FRONTPANEL : BGT_PANEL);
15109 
15110             if(bgl->oldtype == BGT_PANEL)
15111             {
15112                 bgl->order = panelcount + 1;
15113                 __realloc(panels, panelcount);
15114                 panels[panelcount++][0] = level->numlayers;
15115                 bgl->z = PANEL_Z;
15116                 bgl->ratio.x = 0; // x ratio
15117                 bgl->ratio.z = 0; // z ratio
15118                 dm->xrepeat = 1; // x repeat
15119             }
15120             else
15121             {
15122                 frontpanels_loaded++;
15123                 bgl->z = FRONTPANEL_Z;
15124                 bgl->ratio.x = -0.4; // x ratio
15125                 bgl->ratio.z = 1; // z ratio
15126                 dm->xrepeat = -1; // x repeat
15127             }
15128 
15129             bgl->bgspeedratio = 0;
15130             bgl->offset.z = 0;
15131             dm->yrepeat = 1; // z repeat
15132             dm->transbg = 1; // transparency
15133             bgl->enabled = 1; // enabled
15134             bgl->quake = 1; // accept quake and rock
15135 
15136             load_layer(GET_ARG(1), maskPath, level->numlayers);
15137             level->numlayers++;
15138 
15139             if(stricmp(GET_ARG(2), "none") != 0 && GET_ARG(2)[0])
15140             {
15141                 __realloc(level->layers, level->numlayers);
15142                 bgl = &(level->layers[level->numlayers]);
15143                 *bgl = *(bgl - 1);
15144                 panels[panelcount - 1][1] = level->numlayers;
15145 
15146                 bgl->z = NEONPANEL_Z;
15147                 bgl->neon = 1;
15148                 bgl->gfx.handle = NULL;
15149                 load_layer(GET_ARG(2), maskPath, level->numlayers);
15150                 level->numlayers++;
15151             }
15152 
15153             if(stricmp(GET_ARG(3), "none") != 0 && GET_ARG(3)[0])
15154             {
15155                 __realloc(level->layers, level->numlayers);
15156                 bgl = &(level->layers[level->numlayers]);
15157                 *bgl = *(bgl - 1);
15158                 panels[panelcount - 1][2] = level->numlayers;
15159                 dm = &(bgl->drawmethod);
15160 
15161                 bgl->z = SCREENPANEL_Z;
15162                 bgl->neon = 0;
15163                 dm->alpha = BLEND_MODE_ALPHA;
15164                 bgl->gfx.handle = NULL;
15165                 load_layer(GET_ARG(3), maskPath, level->numlayers);
15166                 level->numlayers++;
15167             }
15168             break;
15169         case CMD_LEVEL_STAGENUMBER:
15170             current_stage = GET_INT_ARG(1);
15171             break;
15172         case CMD_LEVEL_ORDER:
15173             // Append to order
15174             value = GET_ARG(1);
15175             i = 0;
15176             while(value[i] )
15177             {
15178                 j = value[i];
15179                 // WTF ?
15180                 if(j >= 'A' && j <= 'Z')
15181                 {
15182                     j -= 'A';
15183                 }
15184                 else if(j >= 'a' && j <= 'z')
15185                 {
15186                     j -= 'a';
15187                 }
15188                 else
15189                 {
15190                     errormessage = "Illegal character in panel order!";
15191                     goto lCleanup;
15192                 }
15193                 __realloc(order, level->numpanels);
15194                 __realloc(level->panels, level->numpanels);
15195                 order[level->numpanels] = j;
15196                 level->numpanels++;
15197                 i++;
15198             }
15199             break;
15200         case CMD_LEVEL_HOLE:
15201             /*value = GET_ARG(1);    // ltb    1-18-05  adjustable hole sprites
15202 
15203             if(holesprite < 0)
15204             {
15205                 if(testpackfile(value, packfile) >= 0)
15206                 {
15207                     holesprite = loadsprite(value, 0, 0, pixelformat);    // ltb 1-18-05  load new hole sprite
15208                 }
15209                 else
15210                 {
15211                     holesprite = loadsprite("data/sprites/hole", 0, 0, pixelformat);    // ltb 1-18-05  no new sprite load the default
15212                 }
15213             }*/
15214 
15215             addhole(GET_FLOAT_ARG(1), GET_FLOAT_ARG(2), GET_FLOAT_ARG(3), GET_FLOAT_ARG(4), GET_FLOAT_ARG(5), GET_FLOAT_ARG(6), GET_FLOAT_ARG(7), GET_FLOAT_ARG(8), GET_FLOAT_ARG(9));
15216             break;
15217         case CMD_LEVEL_WALL:
15218             addwall(GET_FLOAT_ARG(1), GET_FLOAT_ARG(2), GET_FLOAT_ARG(3), GET_FLOAT_ARG(4), GET_FLOAT_ARG(5), GET_FLOAT_ARG(6), GET_FLOAT_ARG(7), GET_FLOAT_ARG(8), GET_FLOAT_ARG(9));
15219             break;
15220         case CMD_LEVEL_BASEMAP:
15221             addbasemap(GET_FLOAT_ARG(1), GET_FLOAT_ARG(2), GET_FLOAT_ARG(3), GET_FLOAT_ARG(4), GET_FLOAT_ARG(5), GET_FLOAT_ARG(6), GET_FLOAT_ARG(7));
15222             break;
15223         case CMD_LEVEL_PALETTE:
15224             __realloc(level->palettes, level->numpalettes);
15225             if(!load_palette(level->palettes[level->numpalettes], GET_ARG(1)))
15226             {
15227                 errormessage = "Failed to create colour conversion tables for level! (Out of memory?)";
15228                 goto lCleanup;
15229             }
15230             level->numpalettes++;
15231             break;
15232         case CMD_LEVEL_UPDATESCRIPT:
15233         case CMD_LEVEL_UPDATEDSCRIPT:
15234         case CMD_LEVEL_KEYSCRIPT:
15235         case CMD_LEVEL_LEVELSCRIPT:
15236         case CMD_LEVEL_ENDLEVELSCRIPT:
15237             switch(cmd)
15238             {
15239             case CMD_LEVEL_UPDATESCRIPT:
15240                 tempscript = &(level->update_script);
15241                 scriptname = "levelupdatescript";
15242                 break;
15243             case CMD_LEVEL_UPDATEDSCRIPT:
15244                 tempscript = &(level->updated_script);
15245                 scriptname = "levelupdatedscript";
15246                 break;
15247             case CMD_LEVEL_KEYSCRIPT:
15248                 tempscript = &(level->key_script);
15249                 scriptname = "levelkeyscript";
15250                 break;
15251             case CMD_LEVEL_LEVELSCRIPT:
15252                 tempscript = &(level->level_script);
15253                 scriptname = "levelscript";
15254                 break;
15255             case CMD_LEVEL_ENDLEVELSCRIPT:
15256                 tempscript = &(level->endlevel_script);
15257                 scriptname = "endlevelscript";
15258                 break;
15259             default:
15260                 assert(0);
15261 
15262             }
15263             // this function is for model script, but it is OK for now
15264             pos += lcmHandleCommandScripts(&arglist, buf + pos, tempscript, scriptname, filename, 1, 1);
15265             break;
15266         case CMD_LEVEL_BLOCKED:
15267             exit_blocked = GET_INT_ARG(1);
15268             break;
15269         case CMD_LEVEL_ENDHOLE:
15270             exit_hole = GET_INT_ARG(1);
15271             break;
15272         case CMD_LEVEL_WAIT:
15273             // Clear spawn thing, set wait state instead
15274             memset(&next, 0, sizeof(next));
15275             next.wait = 1;
15276             break;
15277         case CMD_LEVEL_NOJOIN:
15278             // Clear spawn thing, set nojoin state instead
15279             memset(&next, 0, sizeof(next));
15280             next.nojoin = 1;
15281             break;
15282         case CMD_LEVEL_CANJOIN:
15283             memset(&next, 0, sizeof(next));
15284             next.nojoin = -1;
15285             break;
15286         case CMD_LEVEL_SHADOWCOLOR:
15287             memset(&next, 0, sizeof(next));
15288             next.shadowcolor = parsecolor(GET_ARG(1));
15289             break;
15290         case CMD_LEVEL_SHADOWALPHA:
15291             memset(&next, 0, sizeof(next));
15292             next.shadowalpha = GET_INT_ARG(1);
15293             if(blendfx_is_set == 0 && next.shadowalpha > 0)
15294             {
15295                 blendfx[next.shadowalpha - 1] = 1;
15296             }
15297             break;
15298         case CMD_LEVEL_SHADOWOPACITY:
15299             memset(&next, 0, sizeof(next));
15300             next.shadowopacity = GET_INT_ARG(1);
15301             break;
15302         case CMD_LEVEL_LIGHT:
15303             memset(&next, 0, sizeof(next));
15304             next.light.x = GET_INT_ARG(1);
15305             next.light.y = GET_INT_ARG(2);
15306             if(next.light.y == 0)
15307             {
15308                 next.light.y = 64;
15309             }
15310             break;
15311         case CMD_LEVEL_SCROLLZ:
15312             memset(&next, 0, sizeof(next));
15313             next.scrollminz = GET_INT_ARG(1);
15314             next.scrollmaxz = GET_INT_ARG(2);
15315             next.scrollminz |= 0x80000000;
15316             break;
15317         case CMD_LEVEL_SCROLLX:
15318             //shall we keep blockade?
15319             memset(&next, 0, sizeof(next));
15320             next.scrollminx = GET_INT_ARG(1);
15321             next.scrollmaxx = GET_INT_ARG(2);
15322             next.scrollminx |= 0x80000000;
15323             break;
15324         case CMD_LEVEL_BLOCKADE:
15325             // now x scroll can be limited by this
15326             memset(&next, 0, sizeof(next));
15327             next.blockade = GET_INT_ARG(1);
15328             if(next.blockade == 0)
15329             {
15330                 next.blockade = -1;
15331             }
15332             break;
15333         case CMD_LEVEL_SETPALETTE:
15334             // change system palette
15335             memset(&next, 0, sizeof(next));
15336             next.palette = GET_INT_ARG(1);
15337             break;
15338         case CMD_LEVEL_GROUP:
15339             // Clear spawn thing, set group instead
15340             memset(&next, 0, sizeof(next));
15341             next.groupmin = GET_INT_ARG(1);
15342             next.groupmax = GET_INT_ARG(2);
15343             if(next.groupmax < 1)
15344             {
15345                 next.groupmax = 1;
15346             }
15347             if(next.groupmin < 1)
15348             {
15349                 next.groupmin = 100;
15350             }
15351             break;
15352         case CMD_LEVEL_SPAWN:
15353             // Back to defaults
15354             next.spawnplayer_count = 0;
15355             memset(&next, 0, sizeof(next));
15356             next.index = next.item_properties.index = next.weaponindex = -1;
15357             // Name of entry to be spawned
15358             // Load model (if not loaded already)
15359             cached_model = findmodel(GET_ARG(1));
15360 #ifdef DEBUG
15361             printf("load_level: spawn %s, %s, cached: %p\n", GET_ARG(1), filename, cached_model);
15362 #endif
15363             if(cached_model)
15364             {
15365                 tempmodel = cached_model;
15366             }
15367             else
15368             {
15369                 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
15370             }
15371             if(tempmodel)
15372             {
15373                 next.name = tempmodel->name;
15374                 next.index = get_cached_model_index(next.name);
15375                 next.spawntype = SPAWN_TYPE_LEVEL;  //2011_03_23, DC; Spawntype SPAWN_TYPE_LEVEL.
15376                 crlf = 1;
15377             }
15378             break;
15379         case CMD_LEVEL_2PSPAWN:
15380             // Entity only for 2p game
15381             next.spawnplayer_count = 1;
15382             break;
15383         case CMD_LEVEL_3PSPAWN:
15384             // Entity only for 3p game
15385             next.spawnplayer_count = 2;
15386             break;
15387         case CMD_LEVEL_4PSPAWN:
15388             // Entity only for 4p game
15389             next.spawnplayer_count = 3;
15390             break;
15391         case CMD_LEVEL_BOSS:
15392             next.boss = GET_INT_ARG(1);
15393             level->bossescount += next.boss ? 1 : 0;
15394             level->numbosses = level->bossescount;
15395             break;
15396         case CMD_LEVEL_FLIP:
15397             next.flip = GET_INT_ARG(1);
15398             break;
15399         case CMD_LEVEL_HEALTH:
15400             next.health[0] = next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1);
15401             break;
15402         case CMD_LEVEL_2PHEALTH:
15403             // Health the spawned entity will have if 2 people are playing
15404             next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1);
15405             break;
15406         case CMD_LEVEL_3PHEALTH:
15407             // Health the spawned entity will have if 2 people are playing
15408             next.health[2] = next.health[3] = GET_INT_ARG(1);  //4player
15409             break;
15410         case CMD_LEVEL_4PHEALTH:
15411             // Health the spawned entity will have if 2 people are playing
15412             next.health[3] = GET_INT_ARG(1);  //4player
15413             break;
15414         case CMD_LEVEL_MP:
15415             // mp values to put max mp for player by tails
15416             next.mp = GET_INT_ARG(1);
15417             break;
15418         case CMD_LEVEL_SCORE:
15419             // So score can be overriden in the levels .txt file
15420             next.score = GET_INT_ARG(1);
15421             if(next.score == -1)
15422             {
15423                 next.score = 0;    // So negative values cannot be added
15424             }
15425             next.multiple = GET_INT_ARG(2);
15426             if(next.multiple == -1)
15427             {
15428                 next.multiple = 0;    // So negative values cannot be added
15429             }
15430             break;
15431         case CMD_LEVEL_NOLIFE:
15432             // Flag to determine if entity life is shown when hit
15433             next.nolife = GET_INT_ARG(1);
15434             break;
15435         case CMD_LEVEL_ALIAS:
15436             // Alias (name displayed) of entry to be spawned
15437             strncpy(next.alias, GET_ARG(1), MAX_NAME_LEN - 1);
15438             break;
15439         case CMD_LEVEL_MAP:
15440             // Colourmap for new entry
15441             next.colourmap = GET_INT_ARG(1);
15442             break;
15443         case CMD_LEVEL_ALPHA:
15444             // Item to be contained by new entry
15445             next.alpha = GET_INT_ARG(1);
15446             if(blendfx_is_set == 0 && next.alpha)
15447             {
15448                 blendfx[next.alpha - 1] = 1;
15449             }
15450             break;
15451         case CMD_LEVEL_DYING:
15452             // Used to store which remake corresponds with the dying flash
15453             next.dying = GET_INT_ARG(1);
15454             next.per1 = GET_INT_ARG(2);
15455             next.per2 = GET_INT_ARG(3);
15456             next.dying2 = GET_INT_ARG(4);
15457             break;
15458         case CMD_LEVEL_ITEM:
15459         case CMD_LEVEL_2PITEM:
15460         case CMD_LEVEL_3PITEM:
15461         case CMD_LEVEL_4PITEM:
15462             switch(cmd)
15463             {
15464                 // Item to be contained by new entry
15465             case CMD_LEVEL_ITEM:
15466                 next.item_properties.player_count = 0;
15467                 break;
15468             case CMD_LEVEL_2PITEM:
15469                 next.item_properties.player_count = 1;
15470                 break;
15471             case CMD_LEVEL_3PITEM:
15472                 next.item_properties.player_count = 2;
15473                 break;
15474             case CMD_LEVEL_4PITEM:
15475                 next.item_properties.player_count = 3;
15476                 break;
15477             default:
15478                 assert(0);
15479             }
15480             // Load model (if not loaded already)
15481             cached_model = findmodel(GET_ARG(1));
15482             if(cached_model)
15483             {
15484                 tempmodel = cached_model;
15485             }
15486             else
15487             {
15488                 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
15489             }
15490             if(tempmodel)
15491             {
15492                 next.item = tempmodel->name;
15493                 next.item_properties.index = get_cached_model_index(next.item);
15494             }
15495             break;
15496         case CMD_LEVEL_ITEMMAP:
15497             next.item_properties.colorset = GET_INT_ARG(1);
15498             break;
15499         case CMD_LEVEL_ITEMHEALTH:
15500             next.item_properties.health = GET_INT_ARG(1);
15501             break;
15502         case CMD_LEVEL_ITEMALIAS:
15503             strncpy(next.item_properties.alias, GET_ARG(1), MAX_NAME_LEN - 1);
15504             break;
15505         case CMD_LEVEL_WEAPON:
15506             //spawn with a weapon 2007-2-12 by UTunnels
15507             // Load model (if not loaded already)
15508             cached_model = findmodel(GET_ARG(1));
15509             if(cached_model)
15510             {
15511                 tempmodel = cached_model;
15512             }
15513             else
15514             {
15515                 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
15516             }
15517             if(tempmodel)
15518             {
15519                 next.weapon = tempmodel->name;
15520                 next.weaponindex = get_cached_model_index(next.weapon);
15521             }
15522             break;
15523         case CMD_LEVEL_AGGRESSION:
15524             // Aggression can be set per spawn.
15525             next.aggression = GET_INT_ARG(1);
15526             break;
15527         case CMD_LEVEL_CREDIT:
15528             next.credit = GET_INT_ARG(1);
15529             break;
15530         case CMD_LEVEL_ITEMTRANS:
15531         case CMD_LEVEL_ITEMALPHA:
15532             next.item_properties.alpha = GET_INT_ARG(1);
15533             break;
15534         case CMD_LEVEL_COORDS:
15535             next.position.x = GET_FLOAT_ARG(1);
15536             next.position.z = GET_FLOAT_ARG(2);
15537             next.position.y = GET_FLOAT_ARG(3);
15538             break;
15539         case CMD_LEVEL_SPAWNSCRIPT:
15540             pos += lcmHandleCommandScripts(&arglist, buf + pos, &next.spawnscript, "Level spawn entry script", filename, 0, 1);
15541             break;
15542         case CMD_LEVEL_AT_SCRIPT:
15543             if(!Script_IsInitialized(&next.spawnscript))
15544             {
15545                 Script_Init(&next.spawnscript, "Level spawn entry script", filename, 1);
15546             }
15547             fetchInlineScript(buf, &scriptbuf, &pos, &sblen);
15548             if(!Script_AppendText(&next.spawnscript, scriptbuf, filename))
15549             {
15550                 errormessage = "Unable to parse level spawn entry script.\n";
15551                 goto lCleanup;
15552             }
15553             free(scriptbuf);
15554             scriptbuf = NULL;
15555             break;
15556         case CMD_LEVEL_AT:
15557             // Place entry on queue
15558             next.at = GET_INT_ARG(1);
15559 
15560             if(Script_IsInitialized(&next.spawnscript))
15561             {
15562                 Script_Compile(&next.spawnscript);
15563             }
15564 
15565             __realloc(level->spawnpoints, level->numspawns);
15566             memcpy(&level->spawnpoints[level->numspawns], &next, sizeof(next));
15567             level->numspawns++;
15568 
15569             // And clear...
15570             memset(&next, 0, sizeof(next));
15571             break;
15572         default:
15573             if(command && command[0])
15574             {
15575                 if(!handle_txt_include(command, &arglist, &filename, fnbuf, &buf, &pos, &size))
15576                 {
15577                     printf("Command '%s' not understood in file '%s'!\n", command, filename);
15578                 }
15579             }
15580         }
15581 
15582         // Go to next line
15583         pos += getNewLineStart(buf + pos);
15584 
15585         if(isLoadingScreenTypeBar(bgPosi.set) || isLoadingScreenTypeBg(bgPosi.set))
15586         {
15587             update_loading(&bgPosi, pos, size);
15588         }
15589         //update_loading(bgPosi[0]+videomodes.hShift, bgPosi[1]+videomodes.vShift, bgPosi[2], bgPosi[3]+videomodes.hShift, bgPosi[4]+videomodes.vShift, pos, size, bgPosi[5]);
15590         else
15591         {
15592             update_loading(&loadingbg[1], pos, size);
15593         }
15594     }
15595 
15596     if(level->numpanels < 1)
15597     {
15598         errormessage = "Level error: level has no panels";
15599         goto lCleanup;
15600     }
15601 
15602     if(bgPath[0])
15603     {
15604         clearscreen(vscreen);
15605         spriteq_clear();
15606         load_background(bgPath);
15607     }
15608     else if(background)
15609     {
15610         unload_background();
15611     }
15612 
15613     if(level->numlayers)
15614     {
15615 
15616         for(i = 0; i < level->numlayers; i++)
15617         {
15618             bgl = &(level->layers[i]);
15619             switch(bgl->oldtype)
15620             {
15621             case BGT_WATER: // default water hack
15622                 bgl->offset.z = background ? background->height : level->layers[0].size.y;
15623                 dm = &(bgl->drawmethod);
15624                 if(level->rocking)
15625                 {
15626                     dm->water.watermode = WATER_MODE_SHEAR;
15627                     dm->water.beginsize = 1.0;
15628                     dm->water.endsize = 1 + bgl->size.y / 11.0;
15629                     dm->water.perspective = WATER_PERSPECTIVE_NONE;
15630                     bgl->bgspeedratio = 2;
15631                 }
15632                 break;
15633             case BGT_PANEL:
15634                 panel_width = bgl->size.x;
15635                 panel_height = bgl->size.y;
15636             case BGT_FRONTPANEL:
15637                 if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN))
15638                 {
15639                     bgl->ratio.z = 1;
15640                 }
15641                 break;
15642             case BGT_BACKGROUND:
15643                 bgl->gfx.screen = background;
15644                 bgl->size.x = background->width;
15645                 bgl->size.y = background->height;
15646                 level->background = bgl;
15647                 break;
15648             default:
15649                 break;
15650             }
15651             load_layer(NULL, NULL, i);
15652         }
15653 
15654         if(level->background)
15655         {
15656             __realloc(level->layersref, level->numlayersref);
15657             level->layersref[level->numlayersref] = *(level->background);
15658             level->background = (s_layer *)(size_t)level->numlayersref++;
15659         }
15660 
15661 
15662         // non-panel type layers
15663         for(i = 0; i < level->numlayers; i++)
15664         {
15665             bgl = &(level->layers[i]);
15666             if(bgl->oldtype != BGT_PANEL && bgl->oldtype != BGT_BACKGROUND)
15667             {
15668                 __realloc(level->layersref, level->numlayersref);
15669                 level->layersref[level->numlayersref] = *bgl;
15670                 bgl = &(level->layersref[level->numlayersref]);
15671                 switch(bgl->oldtype)
15672                 {
15673                 case BGT_BGLAYER:
15674                     __realloc(level->bglayers, level->numbglayers);
15675                     level->bglayers[level->numbglayers++] = (s_layer *)(size_t)level->numlayersref;
15676                     break;
15677                 case BGT_FGLAYER:
15678                     __realloc(level->fglayers, level->numfglayers);
15679                     level->fglayers[level->numfglayers++] = (s_layer *)(size_t)level->numlayersref;
15680                     break;
15681                 case BGT_WATER:
15682                     __realloc(level->waters, level->numwaters);
15683                     level->waters[level->numwaters++] = (s_layer *)(size_t)level->numlayersref;
15684                     break;
15685                 case BGT_GENERIC:
15686                     __realloc(level->genericlayers, level->numgenericlayers);
15687                     level->genericlayers[level->numgenericlayers++] = (s_layer *)(size_t)level->numlayersref;
15688                     break;
15689                 case BGT_FRONTPANEL:
15690                     bgl->offset.x = level->numfrontpanels * bgl->size.x;
15691                     bgl->spacing.x = (frontpanels_loaded - 1) * bgl->size.x;
15692                     __realloc(level->frontpanels, level->numfrontpanels);
15693                     level->frontpanels[level->numfrontpanels++] = (s_layer *)(size_t)level->numlayersref;
15694                     break;
15695                 default:
15696                     break;
15697                 }
15698                 level->numlayersref++;
15699             }
15700         }
15701 
15702         //panels, normal neon screen
15703         for(i = 0; i < level->numpanels; i++)
15704         {
15705             for(j = 0; j < 3; j++)
15706             {
15707                 if(panels[order[i]][j])
15708                 {
15709                     __realloc(level->layersref, level->numlayersref);
15710                     level->layersref[level->numlayersref] = level->layers[panels[order[i]][j]];
15711                     bgl = &(level->layersref[level->numlayersref]);
15712                     bgl->offset.x = panel_width * i;
15713                     level->panels[i][j] = (s_layer *)(size_t)level->numlayersref;
15714                     level->numlayersref++;
15715                 }
15716             }
15717         }
15718 
15719         //fix realloc junk pointers
15720         bgl = level->layersref;
15721         level->background = bgl + (size_t)level->background;
15722         for(i = 0; i < level->numpanels; i++)
15723             for(j = 0; j < 3; j++)
15724             {
15725                 level->panels[i][j] = bgl + (size_t)level->panels[i][j];
15726             }
15727         for(i = 0; i < level->numfrontpanels; i++)
15728         {
15729             level->frontpanels[i] = bgl + (size_t)level->frontpanels[i];
15730         }
15731         for(i = 0; i < level->numbglayers; i++)
15732         {
15733             level->bglayers[i] = bgl + (size_t)level->bglayers[i];
15734         }
15735         for(i = 0; i < level->numfglayers; i++)
15736         {
15737             level->fglayers[i] = bgl + (size_t)level->fglayers[i];
15738         }
15739         for(i = 0; i < level->numgenericlayers; i++)
15740         {
15741             level->genericlayers[i] = bgl + (size_t)level->genericlayers[i];
15742         }
15743         for(i = 0; i < level->numwaters; i++)
15744         {
15745             level->waters[i] = bgl + (size_t)level->waters[i];
15746         }
15747 
15748     }
15749 
15750     if(musicPath[0])
15751     {
15752         music(musicPath, 1, musicOffset);
15753     }
15754 
15755     timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
15756     level->width = level->numpanels * panel_width;
15757 
15758     if(level->width < videomodes.hRes)
15759     {
15760         level->width = videomodes.hRes;
15761     }
15762 
15763     scrollmaxx = level->width - videomodes.hRes;
15764     scrollmaxz = panel_height;
15765 
15766     if(level->scrolldir & SCROLL_LEFT)
15767     {
15768         advancex = (float)(level->width - videomodes.hRes);
15769     }
15770     else if(level->scrolldir & SCROLL_INWARD)
15771     {
15772         advancey = (float)(panel_height - videomodes.vRes);
15773     }
15774 
15775     if(exit_blocked)
15776     {
15777         addwall(level->width - 30, PLAYER_MAX_Z, -panel_height, 0, 1000, 1000, panel_height, MAX_WALL_HEIGHT + 1, 0);
15778     }
15779     if(exit_hole)
15780     {
15781         addhole(level->width, PLAYER_MAX_Z, -panel_height, 0, 1000, 1000, panel_height, 0, 0);
15782     }
15783 
15784     if(crlf)
15785     {
15786         printf("\n");
15787     }
15788     printf("Level Loaded:    '%s'\n", level->name);
15789     totalram = getSystemRam(BYTES);
15790     freeram = getFreeRam(BYTES);
15791     usedram = getUsedRam(BYTES);
15792     printf("Total Ram: %11"PRIu64" Bytes ( %5"PRIu64" MB )\n Free Ram: %11"PRIu64" Bytes ( %5"PRIu64" MB )\n Used Ram: %11"PRIu64" Bytes ( %5"PRIu64" MB )\n",
15793         totalram,
15794         totalram >> 20,
15795         freeram,
15796         freeram >> 20,
15797         usedram,
15798         usedram >> 20);
15799     printf("Total sprites mapped: %d\n\n", sprites_loaded);
15800 
15801 lCleanup:
15802 
15803     if (panels)
15804     {
15805         free(panels);
15806     }
15807     if (order)
15808     {
15809         free(order);
15810     }
15811 
15812     if(buf)
15813     {
15814         free(buf);
15815     }
15816     if(scriptbuf)
15817     {
15818         free(scriptbuf);
15819     }
15820 
15821     if(errormessage)
15822     {
15823         borShutdown(1, "ERROR: load_level, file %s, line %d, message: %s", filename, line, errormessage);
15824     }
15825 }
15826 
15827 
15828 
15829 
15830 
15831 /////////////////////////////////////////////////////////////////////////////
15832 //  Status                                                                  //
15833 /////////////////////////////////////////////////////////////////////////////
bar(int x,int y,int value,int maxvalue,s_barstatus * pstatus)15834 void bar(int x, int y, int value, int maxvalue, s_barstatus *pstatus)
15835 {
15836     int max = 100, len, alphabg = 0, bgindex, colourindex;
15837     int forex, forey, forew, foreh, bkw, bkh;
15838     s_drawmethod dm = plainmethod;
15839 
15840     x += pstatus->offset.x;
15841     y += pstatus->offset.y;
15842 
15843     if(pstatus->orientation == HORIZONTALBAR)
15844     {
15845         max = pstatus->size.x;
15846     }
15847     else if(pstatus->orientation == VERTICALBAR)
15848     {
15849         max = pstatus->size.y;
15850     }
15851     else
15852     {
15853         return;
15854     }
15855 
15856     if (value < 0)
15857     {
15858         value = 0;
15859     }
15860 
15861     if (value > maxvalue)
15862     {
15863         value = maxvalue;
15864     }
15865 
15866     if(pstatus->type == VALUEBAR)
15867     {
15868         if(max > maxvalue)
15869         {
15870             max = maxvalue;
15871         }
15872         if(colorbars)
15873         {
15874             if(value <= max / 4)
15875             {
15876                 bgindex = 0;
15877                 colourindex = 1;
15878             }
15879             else if(value <= max / 2)
15880             {
15881                 bgindex = 0;
15882                 colourindex = 2;
15883             }
15884             else if(value <= max)
15885             {
15886                 bgindex = 0;
15887                 colourindex = 3;
15888             }
15889             else
15890             {
15891                 colourindex = value / (max + 1) + 3;
15892                 bgindex = colourindex - 1;
15893             }
15894             if(colourindex > 10)
15895             {
15896                 colourindex = bgindex = 10;
15897             }
15898         }
15899         else
15900         {
15901             colourindex = 2;
15902             bgindex = value > max ? 5 : 1;
15903         }
15904 
15905         len = value % max;
15906         if(!len && value)
15907         {
15908             len = max;
15909         }
15910         alphabg = value > max ? 0 : (BLEND_MULTIPLY + 1);
15911     }
15912     else if(pstatus->type == PERCENTAGEBAR)
15913     {
15914         colourindex = colorbars ? (value * 5 / maxvalue + 1) : 2;
15915         bgindex = colorbars ? 8 : 1;
15916         len = value * max / maxvalue;
15917         if(!len && value)
15918         {
15919             len = 1;
15920         }
15921         alphabg = BLEND_MULTIPLY + 1;
15922     }
15923     else
15924     {
15925         return;
15926     }
15927 
15928     if(pstatus->orientation == HORIZONTALBAR)
15929     {
15930         forex = pstatus->direction == BARSTATUS_DIR_INVERT ? (x + max - len) : x;
15931         forey = y;
15932         forew = len;
15933         bkw = max;
15934         bkh = foreh = pstatus->size.y;
15935     }
15936     else if(pstatus->orientation == VERTICALBAR)
15937     {
15938         forex = x;
15939         forey = pstatus->direction == BARSTATUS_DIR_INVERT ? y : (y + max - len);
15940         bkw = forew = pstatus->size.x;
15941         foreh = len;
15942         bkh = max;
15943     }
15944     else
15945     {
15946         return;
15947     }
15948 
15949     if(!pstatus->colourtable)
15950     {
15951         pstatus->colourtable = &hpcolourtable;
15952     }
15953 
15954     dm.alpha = alphabg;
15955     spriteq_add_box(x + 1, y + 1, bkw, bkh, HUD_Z + 1 + pstatus->backlayer, (*pstatus->colourtable)[bgindex], &dm);
15956     spriteq_add_box(forex + 1, forey + 1, forew, foreh, HUD_Z + 2 + pstatus->barlayer, (*pstatus->colourtable)[colourindex], NULL);
15957 
15958     if(pstatus->noborder == 0)
15959     {
15960         spriteq_add_line(x, y, x + bkw + 1, y, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Top border.
15961         spriteq_add_line(x, y + bkh + 1, x + bkw + 1, y + bkh + 1, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Bottom border.
15962         spriteq_add_line(x, y + 1, x, y + bkh, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Left border.
15963         spriteq_add_line(x + bkw + 1, y + 1, x + bkw + 1, y + bkh, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Right border.
15964         spriteq_add_line(x, y + bkh + 2, x + bkw + 1, y + bkh + 2, HUD_Z + pstatus->borderlayer, color_black, NULL); //Bottom shadow.
15965         spriteq_add_line(x + bkw + 2, y + 1, x + bkw + 2, y + bkh + 2, HUD_Z + pstatus->borderlayer, color_black, NULL); //Right shadow.
15966     }
15967 }
15968 
goto_mainmenu(int flag)15969 void goto_mainmenu(int flag)
15970 {
15971     goto_mainmenu_flag = 1|(flag<<1);
15972     escape_flag = flag; //Kratus (20-04-21) Added the new "escape" flag in the select screen, has the same effect as the esc key but now accessible by the "gotomainmenu" function
15973 }
15974 
backto_mainmenu()15975 void static backto_mainmenu()
15976 {
15977     int i = 0;
15978     s_screen *pausebuffer = allocscreen(videomodes.hRes, videomodes.vRes, PIXEL_32);
15979 
15980     copyscreen(pausebuffer, vscreen);
15981     spriteq_draw(pausebuffer, 0, MIN_INT, MAX_INT, 0, 0);
15982     spriteq_clear();
15983     spriteq_add_screen(0, 0, MIN_INT, pausebuffer, NULL, 0);
15984     spriteq_lock();
15985 
15986     sound_pause_music(1);
15987     sound_pause_sample(1);
15988     _pause = 2;
15989 
15990     if ( (goto_mainmenu_flag&1) ) goto_mainmenu_flag -= 1;
15991 
15992     update(1, 0);
15993 
15994     for(i = 0; i < MAX_PLAYERS; i++)
15995     {
15996         player[i].lives = 0;
15997     }
15998     endgame = 2;
15999     //sound_pause_music(0);
16000     //sound_pause_sample(0);
16001 
16002     _pause = 0;
16003     bothnewkeys = 0;
16004     spriteq_unlock();
16005     spriteq_clear();
16006     freescreen(&pausebuffer);
16007 
16008     return;
16009 }
16010 
pausemenu()16011 void pausemenu()
16012 {
16013     int pauselector = 0;
16014     int quit = 0;
16015     int controlp = 0, i;
16016     int newkeys;
16017     s_set_entry *set = levelsets + current_set;
16018     s_screen *pausebuffer = allocscreen(videomodes.hRes, videomodes.vRes, PIXEL_32);
16019 
16020     copyscreen(pausebuffer, vscreen);
16021     spriteq_draw(pausebuffer, 0, MIN_INT, MAX_INT, 0, 0);
16022     spriteq_clear();
16023     spriteq_add_screen(0, 0, MIN_INT, pausebuffer, NULL, 0);
16024     spriteq_lock();
16025 
16026     for(i = 0; i < set->maxplayers; i++)
16027     {
16028         if(player[i].ent && (player[i].newkeys & FLAG_START))
16029         {
16030             controlp = i;
16031             break;
16032         }
16033     }
16034 
16035     _pause = 2;
16036     bothnewkeys = 0;
16037     while(!quit)
16038     {
16039         _menutextmshift(pauseoffset[4], -2, 0, pauseoffset[5], pauseoffset[6], Tr("Pause"));
16040         _menutextmshift((pauselector == 0)?pauseoffset[1]:pauseoffset[0], -1, 0, pauseoffset[2], pauseoffset[3], Tr("Continue"));
16041         _menutextmshift((pauselector == 1)?pauseoffset[1]:pauseoffset[0],  0, 0, pauseoffset[2], pauseoffset[3], Tr("End Game"));
16042 
16043         update(1, 0);
16044 
16045         newkeys = player[controlp].newkeys;
16046 
16047         if(newkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN))
16048         {
16049             pauselector ^= 1;
16050             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
16051         }
16052         if(newkeys & FLAG_START)
16053         {
16054             if(pauselector)
16055             {
16056                 for(i = 0; i < MAX_PLAYERS; i++)
16057                 {
16058                     player[i].lives = 0;
16059                 }
16060                 endgame = 2;
16061             }
16062             quit = 1;
16063             sound_pause_music(0);
16064             sound_pause_sample(0);
16065             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
16066             pauselector = 0;
16067         }
16068         if(newkeys & FLAG_ESC)
16069         {
16070             quit = 1;
16071             sound_pause_music(0);
16072             sound_pause_sample(0);
16073             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
16074             pauselector = 0;
16075         }
16076         if(newkeys & FLAG_SCREENSHOT)
16077         {
16078             _pause = 1;
16079             sound_pause_music(1);
16080             sound_pause_sample(1);
16081             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
16082             menu_options();
16083         }
16084     }
16085     _pause = 0;
16086     bothnewkeys = 0;
16087     spriteq_unlock();
16088     spriteq_clear();
16089     freescreen(&pausebuffer);
16090 }
16091 
getFPS(void)16092 unsigned getFPS(void)
16093 {
16094     static u64 lasttick = 0, framerate = 0;
16095     u64 curtick = timer_uticks();
16096     if(lasttick > curtick)
16097     {
16098         lasttick = curtick;
16099     }
16100     framerate = (framerate + (curtick - lasttick)) / 2;
16101     lasttick = curtick;
16102     if(!framerate)
16103     {
16104         // if the frame took 0 ms, act like it was 1 ms instead
16105         return 1000;
16106     }
16107 #ifdef PSP
16108     return ((10000000 / framerate) + 9) / 10;
16109 #else
16110     return round(1.0e6 / framerate);
16111 #endif
16112 }
16113 
updatestatus()16114 void updatestatus()
16115 {
16116 
16117     int dt;
16118     int i;
16119     s_model *model = NULL;
16120     s_set_entry *set = levelsets + current_set;
16121 
16122     for(i = 0; i < set->maxplayers; i++)
16123     {
16124         if(player[i].ent)
16125         {
16126             ;
16127         }
16128         else if(player[i].joining && player[i].name[0])
16129         {
16130             model = findmodel(player[i].name);
16131             if((player[i].playkeys & FLAG_ANYBUTTON || skipselect[i][0]) && !freezeall && !nojoin)    // Can't join while animations are frozen
16132             {
16133                 player[i].lives = PLAYER_LIVES;            // to address new lives settings
16134                 player[i].joining = 0;
16135                 player[i].hasplayed = 1;
16136                 player[i].spawnhealth = model->health;
16137                 player[i].spawnmp = model->mp;
16138 
16139                 spawnplayer(i);
16140 
16141                 execute_join_script(i);
16142 
16143                 player[i].disablekeys = player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0;
16144 
16145                 if(!nodropen)
16146                 {
16147                     drop_all_enemies();    //27-12-2004  If drop enemies is on, drop all enemies
16148                 }
16149 
16150                 if(!level->noreset)
16151                 {
16152                     timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
16153                 }
16154 
16155             }
16156             else if(player[i].playkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT))
16157             {
16158                 model = ((player[i].playkeys & FLAG_MOVELEFT) ? prevplayermodeln : nextplayermodeln)(model, i);
16159                 strcpy(player[i].name, model->name);
16160 
16161                 player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmapn(model, -1, i) : 0;
16162 
16163                 player[i].playkeys = 0;
16164             }
16165             // don't like a characters color try a new one!
16166             else if(player[i].playkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN) && colourselect)
16167             {
16168                 player[i].colourmap = ((player[i].playkeys & FLAG_MOVEUP) ? nextcolourmapn : prevcolourmapn)(model, player[i].colourmap, i);
16169 
16170                 player[i].playkeys = 0;
16171             }
16172         }
16173         else if( !nojoin && (player[i].credits || credits || (!player[i].hasplayed && noshare)) )
16174         {
16175             if(player[i].playkeys & FLAG_START)
16176             {
16177                 player[i].lives = 0;
16178                 model = skipselect[i][0] ? findmodel(skipselect[i]) : nextplayermodeln(NULL, i);
16179                 strncpy(player[i].name, model->name, MAX_NAME_LEN);
16180 
16181                 player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmapn(model, -1, i) : 0;
16182 
16183                 player[i].joining = 1;
16184                 player[i].disablekeys = player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0;
16185 
16186                 if(!level->noreset)
16187                 {
16188                     timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
16189                 }
16190 
16191                 if(!player[i].hasplayed && noshare)
16192                 {
16193                     player[i].credits = CONTINUES;
16194                 }
16195 
16196                 if(!creditscheat)
16197                 {
16198                     if(noshare)
16199                     {
16200                         --player[i].credits;
16201                     }
16202                     else
16203                     {
16204                         --credits;
16205                     }
16206                     if(set->continuescore == 1)
16207                     {
16208                         player[i].score = 0;
16209                     }
16210                     if(set->continuescore == 2)
16211                     {
16212                         player[i].score = player[i].score + 1;
16213                     }
16214                 }
16215             }
16216         }
16217     }// end of for
16218 
16219     dt = timeleft / COUNTER_SPEED;
16220     if(dt >= 99)
16221     {
16222         dt      = 99;
16223         oldtime = 99;
16224     }
16225     if(dt <= 0)
16226     {
16227         dt      = 0;
16228         oldtime = 99;
16229     }
16230 
16231     if (is_total_timeover) timetoshow = 0;
16232     else timetoshow = dt;
16233 
16234     if(timetoshow < oldtime || oldtime == 0)
16235     {
16236         execute_timetick_script(timetoshow, go_time);
16237         oldtime = timetoshow;
16238     }
16239 
16240     if(dt > 0 && !is_total_timeover)
16241     {
16242         showtimeover = 0;
16243     }
16244 
16245     if(go_time > _time)
16246     {
16247         dt = (go_time - _time) % GAME_SPEED;
16248 
16249         if(dt < GAME_SPEED / 2)
16250         {
16251             showgo = 1;
16252             if(gosound == 0 )
16253             {
16254 
16255                 if(SAMPLE_GO >= 0)
16256                 {
16257                     sound_play_sample(SAMPLE_GO, 0, savedata.effectvol, savedata.effectvol, 100);    // 26-12-2004 Play go sample as arrow flashes
16258                 }
16259 
16260                 gosound = 1;                // 26-12-2004 Sets sample as already played - stops sample repeating too much
16261             }
16262         }
16263         else
16264         {
16265             showgo = gosound = 0;    //26-12-2004 Resets go sample after loop so it can be played again next time
16266         }
16267     }
16268     else
16269     {
16270         showgo = 0;
16271     }
16272 
16273 }
16274 
16275 
16276 // Caskey, Damon V.
16277 // 2016-11-16
16278 //
16279 // Draw dot onto screen to indicate actual entity position,
16280 // with text readout of Base, X, Y, and Z coordinates directly below.
draw_properties_entity(entity * entity,int offset_z,int color,s_drawmethod * drawmethod)16281 void draw_properties_entity(entity *entity, int offset_z, int color, s_drawmethod *drawmethod)
16282 {
16283     #define FONT_LABEL          1
16284 	#define FONT_VALUE          0
16285 	#define TEXT_MARGIN_Y       1
16286     #define OFFSET_LAYER       -2
16287 
16288     // Array keys for the list of items
16289 	// we want to display
16290     enum
16291     {
16292 		DRAW_PROPERTIES_KEY_NAME,
16293 		DRAW_PROPERTIES_KEY_BASE,
16294 		DRAW_PROPERTIES_KEY_POS,
16295 		DRAW_PROPERTIES_KEY_STATUS,
16296 		DRAW_PROPERTIES_ARRAY_SIZE	// Array size, so always last.
16297     };
16298 
16299     typedef struct
16300     {
16301         s_axis_principal_int        position;
16302         s_axis_plane_vertical_int   size;
16303     } draw_coords;
16304 
16305     s_axis_plane_vertical_int   screen_offset;  // Base location calculated from screen offsets.
16306     s_axis_plane_vertical_int   base_pos;       // Entity position with screen offsets applied.
16307     draw_coords                 box;            // On screen coords for display elements.
16308 
16309     int i;                              // Counter.
16310     int str_offset_x;                   // Calculated offset of text for centering.
16311 	int label_width_max;
16312 	int str_width_max;                  // largest string width.
16313     int str_height_max;                 // Largest string height.
16314     size_t str_size;                    // Memory size of string.
16315 
16316 	char		*output_label[DRAW_PROPERTIES_ARRAY_SIZE];
16317 	const char  *output_format[DRAW_PROPERTIES_ARRAY_SIZE]; // Format ("%d, %s ..").
16318     char        *output_value[DRAW_PROPERTIES_ARRAY_SIZE];  // Final string to display position.
16319 
16320     // Let's build the format for information
16321 	// we want to display.
16322 	output_format[DRAW_PROPERTIES_KEY_NAME]		= "%s";
16323 	output_format[DRAW_PROPERTIES_KEY_BASE]		= "%d";
16324 	output_format[DRAW_PROPERTIES_KEY_POS]		= "%d, %d, %d";
16325 	output_format[DRAW_PROPERTIES_KEY_STATUS]	= "%d, %d";
16326 
16327 	// Double pass method for unknown string size.
16328 	//
16329 	// 1. Build the label.
16330 	//
16331 	// 2. Attempt to copy 0 chars to unallocated
16332 	// buffer and record how many characters
16333 	// would be needed, plus 1 for the NULL terminator
16334 	// and record as a string_size.
16335 	//
16336 	// 3. Allocate memory using the string size.
16337 	//
16338 	// 4. Copy formatted string to allocated buffer
16339 	// for real.
16340 	//
16341 	// Repeat for each line item we want to display.
16342 	//
16343 	// TO: Work this into a loop. Main obstacle is
16344 	// the number of format string inputs vary depending
16345 	// on type of property.
16346 
16347 	// Name
16348 	output_label[DRAW_PROPERTIES_KEY_NAME] = "Name: ";
16349 	output_value[DRAW_PROPERTIES_KEY_NAME] = NULL;
16350 	str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_NAME], 0, output_format[DRAW_PROPERTIES_KEY_NAME], entity->model->name) + 1;
16351 	output_value[DRAW_PROPERTIES_KEY_NAME] = malloc(str_size);
16352 	snprintf(output_value[DRAW_PROPERTIES_KEY_NAME], str_size, output_format[DRAW_PROPERTIES_KEY_NAME], entity->model->name);
16353 
16354 	// Base
16355 	output_label[DRAW_PROPERTIES_KEY_BASE] = "Base: ";
16356 	output_value[DRAW_PROPERTIES_KEY_BASE] = NULL;
16357 	str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_BASE], 0, output_format[DRAW_PROPERTIES_KEY_BASE], (int)entity->base) + 1;
16358 	output_value[DRAW_PROPERTIES_KEY_BASE] = malloc(str_size);
16359 	snprintf(output_value[DRAW_PROPERTIES_KEY_BASE], str_size, output_format[DRAW_PROPERTIES_KEY_BASE], (int)entity->base);
16360 
16361 	// XYZ
16362 	output_label[DRAW_PROPERTIES_KEY_POS] = "X,Y,Z: ";
16363 	output_value[DRAW_PROPERTIES_KEY_POS] = NULL;
16364 	str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_POS], 0, output_format[DRAW_PROPERTIES_KEY_POS], (int)entity->position.x, (int)entity->position.y, (int)entity->position.z) + 1;
16365 	output_value[DRAW_PROPERTIES_KEY_POS] = malloc(str_size);
16366 	snprintf(output_value[DRAW_PROPERTIES_KEY_POS], str_size, output_format[DRAW_PROPERTIES_KEY_POS], (int)entity->position.x, (int)entity->position.y, (int)entity->position.z);
16367 
16368 	// HP & MP
16369 	output_label[DRAW_PROPERTIES_KEY_STATUS] = "HP, MP: ";
16370 	output_value[DRAW_PROPERTIES_KEY_STATUS] = NULL;
16371 	str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_STATUS], 0, output_format[DRAW_PROPERTIES_KEY_STATUS], (int)entity->energy_state.health_current, (int)entity->energy_state.mp_current) + 1;
16372 	output_value[DRAW_PROPERTIES_KEY_STATUS] = malloc(str_size);
16373 	snprintf(output_value[DRAW_PROPERTIES_KEY_STATUS], str_size, output_format[DRAW_PROPERTIES_KEY_STATUS], (int)entity->energy_state.health_current, (int)entity->energy_state.mp_current);
16374 
16375 
16376 	// Get the largest string X and Y space. For X find the largest
16377 	// label and value, then add them. For Y, just get height of
16378 	// largest font.
16379     label_width_max = font_string_width_max(output_label, DRAW_PROPERTIES_ARRAY_SIZE, FONT_LABEL);
16380 	str_width_max = label_width_max + font_string_width_max(output_value, DRAW_PROPERTIES_ARRAY_SIZE, FONT_VALUE);
16381 
16382 	if (fontheight(FONT_LABEL) > fontheight(FONT_VALUE))
16383 	{
16384 		str_height_max = fontheight(FONT_LABEL);
16385 	}
16386 	else
16387 	{
16388 		str_height_max = fontheight(FONT_VALUE);
16389 	}
16390 
16391     // Get our base offsets from screen vs. location.
16392     screen_offset.x = screenx - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
16393     screen_offset.y = screeny - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
16394 
16395     // Get entity position with screen offsets.
16396     base_pos.x = entity->position.x - screen_offset.x;
16397     base_pos.y = (entity->position.z - offset_z) - entity->position.y - screen_offset.y;
16398 
16399     // Get a value of half the text width.
16400     // We can use this to center our text
16401     // on the entity.
16402     str_offset_x = str_width_max / 2;
16403 
16404     // Apply text offset.
16405     box.position.x = base_pos.x - str_offset_x;
16406 
16407     box.position.y = base_pos.y;
16408     box.position.z = entity->position.z + offset_z;
16409 
16410     // Draw position dot.
16411     // The +1 to Z is a quick fix - offset_z
16412     // distorts the dot's vertical position
16413     // instead of just adjusting Z.
16414     spriteq_add_dot(base_pos.x, base_pos.y, box.position.z+1, color, drawmethod);
16415 
16416     // Print each item text.
16417     for(i = 0; i < DRAW_PROPERTIES_ARRAY_SIZE; i++)
16418     {
16419         // If the item string exists then
16420         // we can find a position, print it to
16421         // the screen, and free up allocated memory.
16422         if(output_value[i])
16423         {
16424            // Add font height and margin to Y position.
16425             base_pos.y += (str_height_max + TEXT_MARGIN_Y);
16426 
16427             // Print label to the screen. The value X
16428 			// position adds maximum label width, plus
16429 			// 25% the width a single value character.
16430             font_printf(box.position.x, base_pos.y, FONT_LABEL, OFFSET_LAYER, output_label[i]);
16431 			font_printf(box.position.x + label_width_max + (fontmonowidth(FONT_VALUE) * 0.25), base_pos.y, FONT_VALUE, OFFSET_LAYER, output_value[i]);
16432 
16433             // Release memory allocated for the value strings.
16434             free(output_value[i]);
16435         }
16436     }
16437 
16438     return;
16439 
16440     // Remove local constants.
16441     #undef FONT
16442     #undef TEXT_MARGIN_Y
16443     #undef OFFSET_LAYER
16444 }
16445 
16446 // Caskey, Damon V.
16447 // 2016-11-16
16448 //
16449 // Convert entity's world position to screen
16450 // position and draw a box.
draw_box_on_entity(entity * entity,int pos_x,int pos_y,int pos_z,int size_w,int size_h,int offset_z,int color,s_drawmethod * drawmethod)16451 void draw_box_on_entity(entity *entity, int pos_x, int pos_y, int pos_z, int size_w, int size_h, int offset_z, int color, s_drawmethod *drawmethod)
16452 {
16453     s_axis_plane_vertical_int   screen_offset;  // Base location calculated from screen offsets.
16454     s_axis_plane_vertical_int   base_pos;       // Entity position with screen offsets applied.
16455 
16456     typedef struct
16457     {
16458         s_axis_principal_int        position;
16459         s_axis_plane_vertical_int   size;
16460     } draw_coords;
16461 
16462     draw_coords box;
16463 
16464     // Get our base offsets from screen vs. location.
16465     screen_offset.x = screenx - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
16466     screen_offset.y = screeny - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
16467 
16468     // Get entity position with screen offsets.
16469     base_pos.x = entity->position.x - screen_offset.x;
16470     base_pos.y = (entity->position.z - offset_z) - entity->position.y - screen_offset.y;
16471 
16472     // Now apply drawing coords to position.
16473     box.size.x = size_w - pos_x;
16474 
16475     // Don't forget to accommodate for
16476     // entity direction.
16477     if(entity->direction == DIRECTION_RIGHT)
16478     {
16479         box.position.x = base_pos.x + pos_x;
16480     }
16481     else
16482     {
16483         box.position.x = base_pos.x - (box.size.x + pos_x);
16484     }
16485 
16486     box.position.y = base_pos.y + pos_y;
16487     box.size.y = (base_pos.y + size_h) - box.position.y;
16488 
16489     box.position.z = pos_z + offset_z;
16490 
16491     // Add box to que.
16492     spriteq_add_box(box.position.x, box.position.y, box.size.x, box.size.y, box.position.z, color, drawmethod);
16493 }
16494 
draw_visual_debug()16495 void draw_visual_debug()
16496 {
16497     #define LOCAL_COLOR_BLUE        _makecolour(0, 0, 255)
16498     #define LOCAL_COLOR_GREEN       _makecolour(0, 255, 0)
16499     #define LOCAL_COLOR_MAGENTA     _makecolour(255, 0, 255)
16500     #define LOCAL_COLOR_WHITE       _makecolour(255, 255, 255)
16501 
16502     int i;
16503     int instance;
16504     s_hitbox            *coords;
16505     s_collision_attack  *collision_attack;
16506     s_collision_body    *collision_body;
16507     s_drawmethod        drawmethod = plainmethod;
16508     entity              *entity;
16509 
16510 	int range_y_min = 0;
16511 	int range_y_max = 0;
16512 
16513     drawmethod.alpha = BLEND_MODE_ALPHA;
16514 
16515     for(i=0; i<ent_max; i++)
16516     {
16517         entity = ent_list[i];
16518 
16519         // Entity must exist.
16520         if(!entity)
16521         {
16522             continue;
16523         }
16524 
16525         // Entity must be alive.
16526         if(entity->dead)
16527         {
16528             continue;
16529         }
16530 
16531         // Basic properties (Name, position, HP, etc.).
16532         if(savedata.debuginfo & DEBUG_DISPLAY_PROPERTIES)
16533         {
16534             draw_properties_entity(entity, 0, LOCAL_COLOR_WHITE, NULL);
16535         }
16536 
16537         // Range debug requested?
16538         if(savedata.debuginfo & DEBUG_DISPLAY_RANGE)
16539         {
16540 			// Range is calculated a bit differently than body/attack
16541 			// boxes, which is what the draw_box_on_entity() funciton
16542 			// is meant for. For Y axis, We need to invert the value,
16543 			// and place them in opposiing parameters (Max Y into
16544 			// function's min Y parameter, and and min Y into function's
16545 			// max Y parameter).
16546 
16547 			range_y_min =  -entity->animation->range.y.min;
16548 			range_y_max =  -entity->animation->range.y.max;
16549 
16550             draw_box_on_entity(entity, entity->animation->range.x.min, range_y_max, entity->position.z+1, entity->animation->range.x.max, range_y_min, -1, LOCAL_COLOR_GREEN, &drawmethod);
16551         }
16552 
16553         // Collision body debug requested?
16554         if(savedata.debuginfo & DEBUG_DISPLAY_COLLISION_BODY)
16555         {
16556             // Animation has collision?
16557             if(entity->animation->collision_body)
16558             {
16559                 // Frame has collision?
16560                 if(entity->animation->collision_body[entity->animpos])
16561                 {
16562                     // Loop instances of collision.
16563                     for(instance = 0; instance < max_collisons; instance++)
16564                     {
16565                         // Get collision instance pointer.
16566                         collision_body = entity->animation->collision_body[entity->animpos]->instance[instance];
16567 
16568                         // Valid collision instance pointer found?
16569                         if(collision_body)
16570                         {
16571                             coords = collision_body->coords;
16572                             draw_box_on_entity(entity, coords->x, coords->y, entity->position.z+1, coords->width, coords->height, 2, LOCAL_COLOR_BLUE, &drawmethod);
16573                         }
16574                     }
16575                 }
16576             }
16577         }
16578 
16579         // Collision attack requested?
16580         if(savedata.debuginfo & DEBUG_DISPLAY_COLLISION_ATTACK)
16581         {
16582             // Animation has collision?
16583             if(entity->animation->collision_attack)
16584             {
16585                 // Frame has collision?
16586                 if(entity->animation->collision_attack[entity->animpos])
16587                 {
16588                     // Loop instances of collision.
16589                     for(instance = 0; instance < max_collisons; instance++)
16590                     {
16591                         // Get collision instance pointer.
16592                         collision_attack = entity->animation->collision_attack[entity->animpos]->instance[instance];
16593 
16594                         // Valid collision instance pointer found?
16595                         if(collision_attack)
16596                         {
16597                             coords = collision_attack->coords;
16598                             draw_box_on_entity(entity, coords->x, coords->y, entity->position.z+1, coords->width, coords->height, 2, LOCAL_COLOR_MAGENTA, &drawmethod);
16599                         }
16600                     }
16601                 }
16602             }
16603         }
16604     }
16605 
16606     #undef LOCAL_COLOR_BLUE
16607     #undef LOCAL_COLOR_GREEN
16608     #undef LOCAL_COLOR_MAGENTA
16609     #undef LOCAL_COLOR_WHITE
16610 }
16611 
16612 
predrawstatus()16613 void predrawstatus()
16614 {
16615 
16616     int icon = 0;
16617     int i;
16618     unsigned long tmp;
16619     s_set_entry *set = levelsets + current_set;
16620     s_model *model = NULL;
16621     s_drawmethod drawmethod = plainmethod;
16622 
16623     if(bgicon >= 0)
16624     {
16625         spriteq_add_sprite(videomodes.hShift + bgicon_offsets[0], savedata.windowpos + bgicon_offsets[1], bgicon_offsets[2], bgicon, NULL, 0);
16626     }
16627     if(olicon >= 0)
16628     {
16629         spriteq_add_sprite(videomodes.hShift + olicon_offsets[0], savedata.windowpos + olicon_offsets[1], olicon_offsets[2], olicon, NULL, 0);
16630     }
16631 
16632     for(i = 0; i < set->maxplayers; i++)
16633     {
16634         if(player[i].ent)
16635         {
16636             tmp = player[i].score; //work around issue on 64bit where sizeof(long) != sizeof(int)
16637             if(!pscore[i][2] && !pscore[i][3] && !pscore[i][4] && !pscore[i][5])
16638             {
16639                 font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1], pscore[i][6], 0, (scoreformat ? "%s - %09lu" : "%s - %lu"), (char *)(player[i].ent->name), tmp);
16640             }
16641             else
16642             {
16643                 font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1], pscore[i][6], 0, "%s", player[i].ent->name);
16644                 font_printf(videomodes.shiftpos[i] + pscore[i][2], savedata.windowpos + pscore[i][3], pscore[i][6], 0, "-");
16645                 font_printf(videomodes.shiftpos[i] + pscore[i][4], savedata.windowpos + pscore[i][5], pscore[i][6], 0, (scoreformat ? "%09lu" : "%lu"), tmp);
16646             }
16647 
16648             if(player[i].ent->energy_state.health_current <= 0)
16649             {
16650                 icon = player[i].ent->modeldata.icon.die;
16651             }
16652             else if(player[i].ent->inpain)
16653             {
16654                 icon = player[i].ent->modeldata.icon.pain;
16655             }
16656             else if(player[i].ent->getting)
16657             {
16658                 icon = player[i].ent->modeldata.icon.get;
16659             }
16660             else
16661             {
16662                 icon = player[i].ent->modeldata.icon.def;
16663             }
16664 
16665             if(icon >= 0)
16666             {
16667                 drawmethod.table = player[i].ent->modeldata.icon.usemap ? player[i].ent->colourmap : NULL;
16668                 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], savedata.windowpos + picon[i][1], 10000, icon, &drawmethod, 0);
16669             }
16670 
16671             if(player[i].ent->weapent)
16672             {
16673                 if(player[i].ent->weapent->modeldata.icon.weapon >= 0)
16674                 {
16675                     drawmethod.table = player[i].ent->weapent->modeldata.icon.usemap ? player[i].ent->weapent->colourmap : NULL;
16676                     spriteq_add_sprite(videomodes.shiftpos[i] + piconw[i][0], savedata.windowpos + piconw[i][1], 10000, player[i].ent->weapent->modeldata.icon.weapon, &drawmethod, 0);
16677                 }
16678 
16679                 if(player[i].ent->weapent->modeldata.typeshot && player[i].ent->weapent->modeldata.shootnum)
16680                 {
16681                     font_printf(videomodes.shiftpos[i] + pshoot[i][0], savedata.windowpos + pshoot[i][1], pshoot[i][2], 0, "%u", player[i].ent->weapent->modeldata.shootnum);
16682                 }
16683             }
16684 
16685             if(player[i].ent->modeldata.mp)
16686             {
16687                 drawmethod.table = player[i].ent->modeldata.icon.usemap ? player[i].ent->colourmap : NULL;
16688                 if(player[i].ent->modeldata.icon.mphigh > 0 && (player[i].ent->energy_state.mp_old >= (player[i].ent->modeldata.mp * .66)))
16689                 {
16690                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mphigh, &drawmethod, 0);
16691                 }
16692                 else if(player[i].ent->modeldata.icon.mpmed > 0 && (player[i].ent->energy_state.mp_old >= (player[i].ent->modeldata.mp * .33) && player[i].ent->energy_state.mp_old < (player[i].ent->modeldata.mp * .66)))
16693                 {
16694                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mpmed, &drawmethod, 0);
16695                 }
16696                 else if(player[i].ent->modeldata.icon.mplow > 0 && (player[i].ent->energy_state.mp_old >= 0 && player[i].ent->energy_state.mp_old < (player[i].ent->modeldata.mp * .33)))
16697                 {
16698                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mplow, &drawmethod, 0);
16699                 }
16700                 else if(player[i].ent->modeldata.icon.mphigh > 0 && player[i].ent->modeldata.icon.mpmed == -1 && player[i].ent->modeldata.icon.mplow == -1)
16701                 {
16702                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mphigh, &drawmethod, 0);
16703                 }
16704             }
16705 
16706             font_printf(videomodes.shiftpos[i] + plifeX[i][0], savedata.windowpos + plifeX[i][1], plifeX[i][2], 0, "x");
16707             font_printf(videomodes.shiftpos[i] + plifeN[i][0], savedata.windowpos + plifeN[i][1], plifeN[i][2], 0, "%i", player[i].lives);
16708 
16709             if(rush[0] && player[i].ent->rush.count.current > 1 && _time <= player[i].ent->rush.time)
16710             {
16711                 font_printf(videomodes.shiftpos[i] + prush[i][0], prush[i][1], rush[2], 0, "%s", rush_names[0]);
16712                 font_printf(videomodes.shiftpos[i] + prush[i][2], prush[i][3], rush[3], 0, "%i", player[i].ent->rush.count.current);
16713 
16714                 if(rush[0] != 2)
16715                 {
16716                     font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s", rush_names[1]);
16717                     font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i", player[i].ent->rush.count.max);
16718                 }
16719             }
16720 
16721             if(rush[0] == 2)
16722             {
16723                 font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s", rush_names[1]);
16724                 font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i", player[i].ent->rush.count.max);
16725             }
16726 
16727             if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife)
16728             {
16729                 // Displays life unless overridden by flag
16730                 font_printf(videomodes.shiftpos[i] + ename[i][0], savedata.windowpos + ename[i][1], ename[i][2], 0, player[i].ent->opponent->name);
16731                 if(player[i].ent->opponent->energy_state.health_current <= 0)
16732                 {
16733                     icon = player[i].ent->opponent->modeldata.icon.die;
16734                 }
16735                 else if(player[i].ent->opponent->inpain)
16736                 {
16737                     icon = player[i].ent->opponent->modeldata.icon.pain;
16738                 }
16739                 else if(player[i].ent->opponent->getting)
16740                 {
16741                     icon = player[i].ent->opponent->modeldata.icon.get;
16742                 }
16743                 else
16744                 {
16745                     icon = player[i].ent->opponent->modeldata.icon.def;
16746                 }
16747 
16748                 if(icon >= 0)
16749                 {
16750                     drawmethod.table = player[i].ent->opponent->modeldata.icon.usemap ? player[i].ent->opponent->colourmap : NULL;
16751                     spriteq_add_sprite(videomodes.shiftpos[i] + eicon[i][0], savedata.windowpos + eicon[i][1], 10000, icon, &drawmethod, 0); // Feb 26, 2005 - Changed to opponent->map so icons don't pallete swap with die animation
16752                 }
16753             }
16754         }
16755         else if(player[i].joining && player[i].name[0])
16756         {
16757             model = findmodel(player[i].name);
16758             font_printf(videomodes.shiftpos[i] + pnameJ[i][0], savedata.windowpos + pnameJ[i][1], pnameJ[i][6], 0, player[i].name);
16759             if(nojoin)
16760             {
16761                 font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3], pnameJ[i][6], 0, Tr("Please Wait"));
16762             }
16763             else
16764             {
16765                 font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3], pnameJ[i][6], 0, Tr("Select Hero"));
16766             }
16767             icon = model->icon.def;
16768 
16769             if(icon >= 0)
16770             {
16771                 drawmethod.table = model->icon.usemap ? model_get_colourmap(model, player[i].colourmap) : NULL;
16772                 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], picon[i][1], 10000, icon, &drawmethod, 0);
16773             }
16774         }
16775         else if(player[i].credits || credits || (!player[i].hasplayed && noshare))
16776         {
16777             if(player[i].credits && ((_time / (GAME_SPEED * 2)) & 1))
16778             {
16779                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), player[i].credits);
16780             }
16781             else if(credits && ((_time / (GAME_SPEED * 2)) & 1))
16782             {
16783                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), credits);
16784             }
16785             else if(!player[i].hasplayed  && ((_time / (GAME_SPEED * 2)) & 1))
16786             {
16787                 int showcredits = (!noshare) ? credits : CONTINUES;
16788 
16789                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), showcredits);
16790             }
16791             else if(nojoin)
16792             {
16793                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Please Wait"));
16794             }
16795             else
16796             {
16797                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Press Start"));
16798             }
16799         }
16800         else
16801         {
16802             font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("GAME OVER"));
16803         }
16804     }// end of for
16805 
16806 	// If any of the debug flags are enabled, let's
16807 	// output debug data to end user.
16808     if(savedata.debuginfo)
16809     {
16810         draw_visual_debug();
16811     }
16812 
16813     if(timeicon >= 0)
16814     {
16815         spriteq_add_sprite(videomodes.hShift + timeicon_offsets[0], savedata.windowpos + timeicon_offsets[1], 10000, timeicon, NULL, 0);
16816     }
16817     if(!level->notime)
16818     {
16819         font_printf(videomodes.hShift + timeloc[0] + 2, savedata.windowpos + timeloc[1] + 2, timeloc[5], 0, "%02i", timetoshow);
16820     }
16821     if(showtimeover)
16822     {
16823         font_printf(videomodes.hShift + 113, videomodes.vShift + savedata.windowpos + 110, timeloc[5], 0, Tr("TIME OVER"));
16824     }
16825 
16826     if(showgo)
16827     {
16828         if(level->scrolldir & SCROLL_LEFT) //TODO: upward and downward go
16829         {
16830 
16831             if(golsprite >= 0)
16832             {
16833                 spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, golsprite, NULL, 0);    // new sprite for left direction
16834             }
16835             else
16836             {
16837                 drawmethod.table = 0;
16838                 drawmethod.flipx = 1;
16839                 spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, gosprite, &drawmethod, 0);
16840             }
16841         }
16842         else if(level->scrolldir & SCROLL_RIGHT)
16843         {
16844             spriteq_add_sprite(videomodes.hRes - 40, 60 + videomodes.vShift, 10000, gosprite, NULL, 0);
16845         }
16846     }
16847 
16848     // Performance info.
16849     if(savedata.debuginfo & DEBUG_DISPLAY_PERFORMANCE)
16850     {
16851         spriteq_add_box(0, videomodes.dOffset - 12, videomodes.hRes, videomodes.dOffset + 12, LAYER_Z_LIMIT_BOX_MAX, 0, NULL);
16852         font_printf(2, videomodes.dOffset - 10, 0, LAYER_Z_LIMIT_MAX, Tr("FPS: %03d"), getFPS());
16853         font_printf(videomodes.hRes / 2, videomodes.dOffset - 10, 0, LAYER_Z_LIMIT_MAX, Tr("Free Ram: %s KB"), commaprint(freeram / KBYTES));
16854         font_printf(2, videomodes.dOffset, 0, LAYER_Z_LIMIT_MAX, Tr("Sprites: %d / %d"), spriteq_get_sprite_count(), spriteq_get_sprite_max());
16855         font_printf(videomodes.hRes / 2, videomodes.dOffset, 0, LAYER_Z_LIMIT_MAX, Tr("Used Ram: %s KB"), commaprint(usedram / KBYTES));
16856     }
16857 }
16858 
16859 // draw boss status on screen
drawenemystatus(entity * ent)16860 void drawenemystatus(entity *ent)
16861 {
16862     s_drawmethod drawmethod;
16863     int icon;
16864 
16865     if(ent->modeldata.namex > -1000 && ent->modeldata.namey > -1000)
16866     {
16867         font_printf(ent->modeldata.namex, ent->modeldata.namey, 0, 0, "%s", ent->name);
16868     }
16869 
16870     if(ent->modeldata.icon.position.x > -1000 &&  ent->modeldata.icon.position.y > -1000)
16871     {
16872         if(ent->energy_state.health_current <= 0)
16873         {
16874             icon = ent->modeldata.icon.die;
16875         }
16876         else if(ent->inpain)
16877         {
16878             icon = ent->modeldata.icon.pain;
16879         }
16880         else if(ent->getting)
16881         {
16882             icon = ent->modeldata.icon.get;
16883         }
16884         else
16885         {
16886             icon = ent->modeldata.icon.def;
16887         }
16888 
16889         if(icon >= 0)
16890         {
16891             drawmethod = plainmethod;
16892             drawmethod.table = ent->modeldata.icon.usemap ? ent->colourmap : NULL;
16893             spriteq_add_sprite(ent->modeldata.icon.position.x, ent->modeldata.icon.position.y, HUD_Z, icon, &drawmethod, 0);
16894         }
16895     }
16896 
16897     if(ent->modeldata.health && ent->modeldata.hpx > -1000 && ent->modeldata.hpy > -1000)
16898     {
16899         bar(ent->modeldata.hpx, ent->modeldata.hpy, ent->energy_state.health_old, ent->modeldata.health, &(ent->modeldata.hpbarstatus));
16900     }
16901 }
16902 
16903 
drawstatus()16904 void drawstatus()
16905 {
16906     int i;
16907 
16908     for(i = 0; i < MAX_PLAYERS; i++)
16909     {
16910         if(player[i].ent)
16911         {
16912             // Health bars
16913             bar(videomodes.shiftpos[i] + plife[i][0], savedata.windowpos + plife[i][1], player[i].ent->energy_state.health_old, player[i].ent->modeldata.health, &lbarstatus);
16914             if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife && player[i].ent->opponent->modeldata.health)
16915             {
16916                 bar(videomodes.shiftpos[i] + elife[i][0], savedata.windowpos + elife[i][1], player[i].ent->opponent->energy_state.health_old, player[i].ent->opponent->modeldata.health, &olbarstatus);    // Tied in with the nolife flag
16917             }
16918             // Draw mpbar
16919             if(player[i].ent->modeldata.mp)
16920             {
16921                 bar(videomodes.shiftpos[i] + pmp[i][0], savedata.windowpos + pmp[i][1], player[i].ent->energy_state.mp_old, player[i].ent->modeldata.mp, &mpbarstatus);
16922             }
16923         }
16924     }
16925 
16926     // Time box
16927     if(!level->notime && !timeloc[4])    // Only draw if notime is set to 0 or not specified
16928     {
16929         spriteq_add_line(videomodes.hShift + timeloc[0],                savedata.windowpos + timeloc[1],                videomodes.hShift + timeloc[0] + timeloc[2],     savedata.windowpos + timeloc[1],                HUD_Z, color_black, NULL);
16930         spriteq_add_line(videomodes.hShift + timeloc[0],                savedata.windowpos + timeloc[1],                videomodes.hShift + timeloc[0],                savedata.windowpos + timeloc[1] + timeloc[3],     HUD_Z,  color_black, NULL);
16931         spriteq_add_line(videomodes.hShift + timeloc[0] + timeloc[2],     savedata.windowpos + timeloc[1],                videomodes.hShift + timeloc[0] + timeloc[2],     savedata.windowpos + timeloc[1] + timeloc[3],     HUD_Z,  color_black, NULL);
16932         spriteq_add_line(videomodes.hShift + timeloc[0],                savedata.windowpos + timeloc[1] + timeloc[3],     videomodes.hShift + timeloc[0] + timeloc[2],     savedata.windowpos + timeloc[1] + timeloc[3],     HUD_Z, color_black, NULL);
16933         spriteq_add_line(videomodes.hShift + timeloc[0] - 1,            savedata.windowpos + timeloc[1] - 1,            videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] - 1,            HUD_Z + 1,   color_white, NULL);
16934         spriteq_add_line(videomodes.hShift + timeloc[0] - 1,            savedata.windowpos + timeloc[1] - 1,            videomodes.hShift + timeloc[0] - 1,            savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1,  color_white, NULL);
16935         spriteq_add_line(videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] - 1,            videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1,  color_white, NULL);
16936         spriteq_add_line(videomodes.hShift + timeloc[0] - 1,            savedata.windowpos + timeloc[1] + timeloc[3] - 1, videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1, color_white, NULL);
16937     }
16938 }
16939 
update_loading(s_loadingbar * s,int value,int max)16940 void update_loading(s_loadingbar *s,  int value, int max)
16941 {
16942     static unsigned int lasttick = 0;
16943     static unsigned int soundtick = 0;
16944     static unsigned int keybtick = 0;
16945     int pos_x = s->bar_position.x + videomodes.hShift;
16946     int pos_y = s->bar_position.y + videomodes.vShift;
16947     int size_x = s->bsize;
16948     int text_x = s->text_position.x + videomodes.hShift;
16949     int text_y = s->text_position.y + videomodes.vShift;
16950     unsigned int ticks = timer_gettick();
16951 #ifdef PSP
16952     ticks /= 1000; //temp solution
16953 #endif
16954     if(ticks - soundtick > 20)
16955     {
16956         sound_update_music();
16957         soundtick = ticks;
16958     }
16959 
16960     if(ticks - keybtick > 250)
16961     {
16962         control_update(playercontrolpointers, 1); // Respond to exit and/or fullscreen requests from user/OS
16963         keybtick = ticks;
16964     }
16965 
16966 
16967     if(ticks - lasttick > s->refreshMs || value < 0 || value == max)   // Negative value forces a repaint. used when only bg is drawn for the first time
16968     {
16969         spriteq_clear();
16970         execute_loading_script(value, max);
16971         if(s->set)
16972         {
16973             if (value < 0)
16974             {
16975                 value = 0;
16976             }
16977             if(isLoadingScreenTypeBar(s->set))
16978             {
16979                 loadingbarstatus.size.x = size_x;
16980                 bar(pos_x, pos_y, value, max, &loadingbarstatus);
16981             }
16982             font_printf(text_x, text_y, s->tf, 0, Tr("Loading..."));
16983             if(isLoadingScreenTypeBg(s->set))
16984             {
16985                 if(background)
16986                 {
16987                     putscreen(vscreen, background, 0, 0, NULL);
16988                 }
16989                 else
16990                 {
16991                     clearscreen(vscreen);
16992                 }
16993             }
16994             spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0);
16995             video_copy_screen(vscreen);
16996             spriteq_clear();
16997         }
16998         else if(value < 0)   // Original BOR v1.0029 used this method.  Since loadingbg is optional, we should print this one again.
16999         {
17000             clearscreen(vscreen);
17001             font_printf(120 + videomodes.hShift, 110 + videomodes.vShift, 0, 0, Tr("Loading..."));
17002             spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0);
17003             video_copy_screen(vscreen);
17004             spriteq_clear();
17005         }
17006         lasttick = ticks;
17007     }
17008 }
17009 
addscore(int playerindex,int add)17010 void addscore(int playerindex, int add)
17011 {
17012     unsigned int s = 0;
17013     unsigned int next1up = 0;
17014     ScriptVariant var; // used for execute script
17015     Script *cs;
17016 
17017     if(playerindex < 0)
17018     {
17019         return;    //dont score if <0, e.g., npc damage enemy, enemy damage enemy
17020     }
17021 
17022     playerindex &= 3;
17023 
17024     s = player[playerindex].score;
17025     cs = score_script + playerindex;
17026 
17027     if (lifescore > 0) next1up = ((s / lifescore) + 1) * lifescore;
17028 	else lifescore = 0;
17029 
17030     s += add;
17031     if(s > 999999999)
17032     {
17033         s = 999999999;
17034     }
17035 
17036     while(s > next1up)
17037     {
17038 
17039         if(SAMPLE_1UP >= 0)
17040         {
17041             sound_play_sample(SAMPLE_1UP, 0, savedata.effectvol, savedata.effectvol, 100);
17042         }
17043 
17044         player[playerindex].lives++;
17045         next1up += lifescore;
17046     }
17047 
17048     player[playerindex].score = s;
17049 
17050     //execute a script then
17051     if(Script_IsInitialized(cs))
17052     {
17053         ScriptVariant_Init(&var);
17054         ScriptVariant_ChangeType(&var, VT_INTEGER);
17055         var.lVal = (LONG)add;
17056         Script_Set_Local_Variant(cs, "score", &var);
17057         Script_Execute(cs);
17058         ScriptVariant_Clear(&var);
17059         Script_Set_Local_Variant(cs, "score", &var);
17060     }
17061 }
17062 
17063 
17064 
17065 
17066 // ---------------------------- Object handling ------------------------------
17067 
free_ent(entity * e)17068 void free_ent(entity *e)
17069 {
17070     if(!e)
17071     {
17072         return;
17073     }
17074     clear_all_scripts(e->scripts, 2);
17075     free_all_scripts(&e->scripts);
17076 
17077     // Item properties.
17078     if(e->item_properties)
17079     {
17080         free(e->item_properties);
17081         e->item_properties = NULL;
17082     }
17083 
17084 	// Recursive damage (damage over time).
17085 	if (e->recursive_damage)
17086 	{
17087 		free_recursive_list(e->recursive_damage);
17088 		e->recursive_damage = NULL;
17089 	}
17090 
17091     if(e->waypoints)
17092     {
17093         free(e->waypoints);
17094         e->waypoints = NULL;
17095     }
17096     if(e->defense)
17097     {
17098         free(e->defense);
17099         e->defense = NULL;
17100     }
17101     if(e->offense_factors)
17102     {
17103         free(e->offense_factors);
17104         e->offense_factors = NULL;
17105     }
17106 
17107 	if (e->drawmethod)
17108 	{
17109 		free(e->drawmethod);
17110 		e->drawmethod = NULL;
17111 	}
17112 
17113     if(e->varlist)
17114     {
17115         // Although free_ent will be only called once when the engine is shutting down,
17116         // just clear those in case we forget something
17117         Varlist_Clear(e->varlist);
17118         free(e->varlist);
17119         e->varlist = NULL;
17120     }
17121     free(e);
17122     e = NULL;
17123 }
17124 
free_ents()17125 void free_ents()
17126 {
17127     int i;
17128     if(!ent_list)
17129     {
17130         return;
17131     }
17132     for(i = 0; i < ent_list_size; i++)
17133     {
17134         free_ent(ent_list[i]);
17135     }
17136     free(ent_list);
17137     free(ent_stack);
17138     ent_list = ent_stack = NULL;
17139     ent_list_size = ent_max = ent_count = ent_stack_size = 0;
17140 }
17141 
alloc_ent()17142 entity *alloc_ent()
17143 {
17144     entity *ent = malloc(sizeof(*ent));
17145     memset(ent, 0, sizeof(*ent));
17146     ent->defense = malloc(sizeof(*ent->defense) * max_attack_types);
17147     memset(ent->defense, 0, sizeof(*ent->defense)*max_attack_types);
17148     ent->offense_factors = malloc(sizeof(*ent->offense_factors) * max_attack_types);
17149     memset(ent->offense_factors, 0, sizeof(*ent->offense_factors)*max_attack_types);
17150     ent->varlist = calloc(1, sizeof(*ent->varlist));
17151     // memset should be OK by know, because VT_EMPTY is zero by value, or else we should use ScriptVariant_Init
17152     Varlist_Init(ent->varlist, max_entity_vars);
17153     alloc_all_scripts(&ent->scripts);
17154     return ent;
17155 }
17156 
alloc_ents()17157 int alloc_ents()
17158 {
17159     int i;
17160 
17161     if(ent_list_size >= maxentities)
17162     {
17163         return 0;
17164     }
17165 
17166     ent_list_size += MAX_ENTS;
17167 
17168     ent_list = realloc(ent_list, sizeof(*ent_list) * ent_list_size);
17169     ent_stack = realloc(ent_stack, sizeof(*ent_list) * ent_list_size);
17170 
17171     for(i = ent_list_size - MAX_ENTS; i < ent_list_size; i++)
17172     {
17173         ent_list[i] = alloc_ent();
17174         ent_list[i]->sortid = i * 100;
17175         ent_stack[i] = NULL;
17176     }
17177     //ent_count = ent_max = 0;
17178     return 1;
17179 }
17180 
is_walking(int iAni)17181 int is_walking(int iAni)
17182 {
17183     int i;
17184 
17185     for(i = 0; i < max_downs; i++)
17186     {
17187         if (iAni == animdowns[i]) return 1;
17188     }
17189     for(i = 0; i < max_ups; i++)
17190     {
17191         if (iAni == animups[i]) return 1;
17192     }
17193     for(i = 0; i < max_walks; i++)
17194     {
17195         if (iAni == animwalks[i]) return 1;
17196     }
17197     for(i = 0; i < max_backwalks; i++)
17198     {
17199         if (iAni == animbackwalks[i]) return 1;
17200     }
17201 
17202     return 0;
17203 }
17204 
17205 // UT: merged DC's common walk/idle functions
17206 //
17207 // Caskey, Damon V.
17208 // 2019-02-09
17209 //
17210 // Rewritten for greater readability.
common_anim_series(entity * ent,e_animations * alterates,int max_alternates,int force_mode,e_animations default_animation)17211 static bool common_anim_series(entity *ent, e_animations *alterates, int max_alternates, int force_mode, e_animations default_animation)
17212 {
17213 	int i;						// Loop cursor.
17214 	int loop_min;
17215 	int loop_max;
17216 	e_animations animation_id;	// Animation to apply.
17217 
17218 	// If we have a forced mode, we'll use it to constrict
17219 	// loop options to just the forced mode.
17220 	loop_min = force_mode ? force_mode - 1 : 0;
17221 	loop_max = force_mode ? force_mode : max_alternates;
17222 
17223 	// Loop through available types of series animations (max idles/walks/etc.).
17224     for (i = loop_min; i < loop_max; i++)
17225     {
17226 		// Get animation from array of alternates.
17227 		animation_id = alterates[i];
17228 
17229 		// If we don't have the selected animation, just
17230 		// get out of this loop iteration.
17231 		if (!validanim(ent, animation_id))
17232 		{
17233 			continue;
17234 		}
17235 
17236 		// Can't be the default animation.
17237 		if (animation_id == default_animation)
17238 		{
17239 			continue;
17240 		}
17241 
17242 		// If there's a target in range of this alternate animation, or
17243 		// we're forced to use it, switch animations.
17244 		if (force_mode || normal_find_target(animation_id, 0))
17245 		{
17246 			ent->ducking = DUCK_NONE;
17247 			ent_set_anim(ent, animation_id, 0);
17248 			if (is_walking(animation_id)) ent->walking = 1;
17249 
17250 			// Return true.
17251 			return 1;
17252 		}
17253     }
17254 
17255 	// No alternates were set. Use default if we have it.
17256     if (validanim(ent, default_animation))
17257     {
17258 		ent->ducking = DUCK_NONE;
17259 		ent_set_anim(ent, default_animation, 0);
17260 		if (is_walking(default_animation)) ent->walking = 1;
17261 
17262 		// Return true.
17263 		return 1;
17264     }
17265 
17266     return 0;
17267 }
17268 
common_idle_anim(entity * ent)17269 int common_idle_anim(entity *ent)
17270 {
17271     /*
17272     common_idle_anim
17273     Damon Vaughn Caskey
17274     11012009
17275     Determine and set appropriate idle animation based on condition and range.
17276     Returns 1 if any animation is set.
17277     */
17278     entity *tempself = self;
17279 
17280     self = ent;
17281 
17282     self->ducking = DUCK_NONE;
17283     if(self->idling)
17284     {
17285         self->idling |= IDLING_ACTIVE;
17286     }
17287 
17288     if (ent->model->subtype != SUBTYPE_BIKER && ent->model->type != TYPE_NONE) // biker fix by Plombo // type none being "idle" prevented contra locked and loaded from working correctly. fixed by anallyst
17289     {
17290         ent->velocity.x = ent->velocity.z = 0;    //Stop movement.
17291     }
17292 
17293     if(validanim(ent, ANI_FAINT) && ent->energy_state.health_current <= ent->modeldata.health / 4)           //ANI_FAINT and health at/below 25%?
17294     {
17295         ent_set_anim(ent, ANI_FAINT, 0);                                                //Set ANI_FAINT.
17296         goto found;                                                                      //Return 1 and exit.
17297     }
17298     else if(validanim(ent, ANI_SLEEP) && _time > ent->sleeptime)    //ANI_SLEEP, sleeptime up
17299     {
17300         ent_set_anim(ent, ANI_SLEEP, 0);                                                //Set sleep anim.
17301         goto found;                                                                     //Return 1 and exit.
17302     }
17303     else if(validanim(ent, ANI_EDGE) && ent->edge && (ent->idling & IDLING_ACTIVE) && ent->ducking == DUCK_NONE)
17304     {
17305         if ( (ent->edge & EDGE_RIGHT) && (ent->edge & EDGE_LEFT) )
17306         {
17307             ent_set_anim(ent, ANI_EDGE, 0);
17308         }
17309         else if (ent->edge & EDGE_RIGHT)
17310         {
17311             if (ent->direction == DIRECTION_RIGHT)
17312             {
17313                 ent_set_anim(ent, ANI_EDGE, 0);
17314             }
17315             else
17316             {
17317                 if(validanim(ent, ANI_BACKEDGE)) ent_set_anim(ent, ANI_BACKEDGE, 0);
17318                 else ent_set_anim(ent, ANI_EDGE, 0);
17319             }
17320         }
17321         else if (ent->edge & EDGE_LEFT)
17322         {
17323             if (ent->direction == DIRECTION_LEFT)
17324             {
17325                 ent_set_anim(ent, ANI_EDGE, 0);
17326             }
17327             else
17328             {
17329                 if(validanim(ent, ANI_BACKEDGE)) ent_set_anim(ent, ANI_BACKEDGE, 0);
17330                 else ent_set_anim(ent, ANI_EDGE, 0);
17331             }
17332         }
17333         goto found;                                                                     //Return 1 and exit.
17334     }
17335     else if(common_anim_series(ent, animidles, max_idles, ent->idlemode, ANI_IDLE))
17336     {
17337         goto found;
17338     }
17339 
17340     self = tempself;
17341     return 0;
17342 found:
17343     self = tempself;
17344     return 1;
17345 }
17346 
17347 
17348 #define common_walk_anim(ent) \
17349 	common_anim_series(ent, animwalks, max_walks, ent->walkmode, ANI_WALK)
17350 
17351 #define common_backwalk_anim(ent) \
17352 	common_anim_series(ent, animbackwalks, max_backwalks, ent->walkmode, ANI_BACKWALK)
17353 
17354 #define common_up_anim(ent) \
17355 	common_anim_series(ent, animups, max_ups, ent->walkmode, ANI_UP)
17356 
17357 #define common_down_anim(ent) \
17358 	common_anim_series(ent, animdowns, max_downs, ent->walkmode, ANI_DOWN)
17359 
17360 
17361 // This function find nearst x escape point from the given position
17362 // It assumes the point is already inside the wall
find_nearest_wall_x(int wall,float x,float z)17363 static float find_nearest_wall_x(int wall, float x, float z)
17364 {
17365     float x1, x2;
17366 
17367     x1 = level->walls[wall].x + level->walls[wall].lowerleft + (level->walls[wall].z - z) * ((level->walls[wall].upperleft - level->walls[wall].lowerleft) / level->walls[wall].depth);
17368     x2 = level->walls[wall].x + level->walls[wall].lowerright + (level->walls[wall].z - z) * ((level->walls[wall].upperright - level->walls[wall].lowerright) / level->walls[wall].depth);
17369 
17370     if(diff(x1, x) > diff(x2, x))
17371     {
17372         return x2;
17373     }
17374     else
17375     {
17376         return x1;
17377     }
17378 }
17379 
17380 // this method initialize an entity's A.I. behaviors
ent_default_init(entity * e)17381 void ent_default_init(entity *e)
17382 {
17383     int dodrop;
17384     int wall;
17385     entity *other;
17386 
17387     if(!e)
17388     {
17389         return;
17390     }
17391 
17392     if((!selectScreen && !_time) || e->modeldata.type != TYPE_PLAYER )
17393     {
17394         if( validanim(e, ANI_SPAWN))
17395         {
17396             ent_set_anim(e, ANI_SPAWN, 0);    // use new playerselect spawn anim
17397         }
17398         else if( validanim(e, ANI_RESPAWN))
17399         {
17400             ent_set_anim(e, ANI_RESPAWN, 0);
17401         }
17402         //else set_idle(e);
17403     }
17404     else if(!selectScreen && _time && e->modeldata.type == TYPE_PLAYER) // mid-level respawn
17405     {
17406         if( validanim(e, ANI_RESPAWN))
17407         {
17408             ent_set_anim(e, ANI_RESPAWN, 0);
17409         }
17410         else if( validanim(e, ANI_SPAWN))
17411         {
17412             ent_set_anim(e, ANI_SPAWN, 0);
17413         }
17414         //else set_idle(e);
17415     }
17416     else if(selectScreen && validanim(e, ANI_SELECT))
17417     {
17418 		// Play transition if we have one. Default Select otherwise.
17419 		if (validanim(e, ANI_SELECTIN))
17420 		{
17421 			ent_set_anim(e, ANI_SELECTIN, 0);
17422 		}
17423 		else
17424 		{
17425 			ent_set_anim(e, ANI_SELECT, 0);
17426 		}
17427 	}
17428     //else set_idle(e);
17429 
17430     if(!level)
17431     {
17432         if(!e->animation)
17433         {
17434             set_idle(e);
17435         }
17436         return;
17437     }
17438 
17439     e->nograb_default = 0; // init all entities to 0 by default
17440 
17441     switch(e->modeldata.type)
17442     {
17443     case TYPE_RESERVED:
17444         //Do nothing.
17445         break;
17446     case TYPE_ENDLEVEL:
17447     case TYPE_ITEM:
17448         e->nograb = 1;
17449         e->nograb_default = e->nograb;
17450         break;
17451 
17452     case TYPE_PLAYER:
17453         //e->direction = (level->scrolldir != SCROLL_LEFT);
17454         e->takedamage = player_takedamage;
17455         e->think = player_think;
17456         e->trymove = player_trymove;
17457 
17458         if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN))
17459         {
17460             e->takeaction = common_spawn;
17461         }
17462         else if(!e->animation)
17463         {
17464             int player_index = (int)e->playerindex;
17465 
17466             if(player_index < levelsets[current_set].maxplayers && level->spawn && _time && level->spawn[player_index].y > e->position.y)
17467             {
17468                 e->takeaction = common_drop;
17469                 e->position.y = (float)level->spawn[player_index].y;
17470                 if(validanim(e, ANI_JUMP))
17471                 {
17472                     ent_set_anim(e, ANI_JUMP, 0);
17473                 }
17474             }
17475         }
17476         if(_time && e->modeldata.makeinv)
17477         {
17478             // Spawn invincible code
17479             e->invincible |= INVINCIBLE_INTANGIBLE;
17480             e->blink = (e->modeldata.makeinv > 0);
17481             e->invinctime = _time + ABS(e->modeldata.makeinv);
17482             e->arrowon = 1;    // Display the image above the player
17483         }
17484         break;
17485     case TYPE_NPC: // use NPC(or A.I. player) instread of an enemy subtype or trap subtype, for further A.I. use
17486         if(e->modeldata.multiple == 0)
17487         {
17488             e->modeldata.multiple = -1;
17489         }
17490 
17491     case TYPE_ENEMY:
17492         e->think = common_think;
17493         if(e->modeldata.subtype == SUBTYPE_BIKER)
17494         {
17495             e->nograb = 1;
17496             e->nograb_default = e->nograb;
17497             e->attacking = ATTACKING_ACTIVE;
17498             //e->direction = (e->position.x<0);
17499             if(e->modeldata.speed)
17500             {
17501                 e->velocity.x = (e->direction == DIRECTION_RIGHT) ? (e->modeldata.speed) : (-e->modeldata.speed);
17502             }
17503             else
17504             {
17505                 e->velocity.x = (e->direction == DIRECTION_RIGHT) ? (1.7 + randf((float)0.6)) : (-(1.7 + randf((float)0.6)));
17506             }
17507             e->takedamage = biker_takedamage;
17508             e->speedmul = 2;
17509             break;
17510         }
17511         // define new subtypes
17512         else if(e->modeldata.subtype == SUBTYPE_ARROW)
17513         {
17514             e->energy_state.health_current = 1;
17515             if(!e->modeldata.speed && !e->modeldata.nomove)
17516             {
17517                 e->modeldata.speed = 2;    // Set default speed to 2 for arrows
17518             }
17519             else if(e->modeldata.nomove)
17520             {
17521                 e->modeldata.speed = 0;
17522             }
17523             if(e->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR)
17524             {
17525                 e->base = 0;
17526             }
17527             else
17528             {
17529                 e->base = e->position.y;
17530             }
17531             e->nograb = 1;
17532             e->nograb_default = e->nograb;
17533             e->attacking = ATTACKING_ACTIVE;
17534             e->takedamage = arrow_takedamage;
17535             e->speedmul = 2;
17536             break;
17537         }
17538         else
17539         {
17540             e->trymove = common_trymove;
17541             // Must just be a regular enemy, set defaults accordingly
17542             if(!e->modeldata.speed && !e->modeldata.nomove)
17543             {
17544                 e->modeldata.speed = 1;
17545             }
17546             else if(e->modeldata.nomove)
17547             {
17548                 e->modeldata.speed = 0;
17549             }
17550             if(e->modeldata.multiple == 0)
17551             {
17552                 e->modeldata.multiple = 5;
17553             }
17554             e->takedamage = common_takedamage;//enemy_takedamage;
17555         }
17556 
17557         if(e->modeldata.subtype == SUBTYPE_NOTGRAB)
17558         {
17559             e->nograb = 1;
17560             e->nograb_default = e->nograb;
17561         }
17562 
17563         if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN))
17564         {
17565             e->takeaction = common_spawn;
17566         }
17567         else
17568         {
17569             dodrop = (e->modeldata.subtype != SUBTYPE_ARROW && level && (level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN));
17570 
17571             if(!nodropspawn && (dodrop || (e->position.x > advancex - 30 && e->position.x < advancex + videomodes.hRes + 30 && e->position.y == 0)) )
17572             {
17573                 e->position.y += videomodes.vRes + randf(40);
17574             }
17575             if(inair(e))
17576             {
17577                 e->takeaction = common_drop;//enemy_drop;
17578                 if(validanim(e, ANI_JUMP))
17579                 {
17580                     ent_set_anim(e, ANI_JUMP, 0);
17581                 }
17582             }
17583         }
17584         break;
17585         // define trap type
17586     case TYPE_TRAP:
17587         e->think = trap_think;
17588         e->takedamage =  common_takedamage;//enemy_takedamage;
17589         e->speedmul = 2;
17590         break;
17591     case TYPE_OBSTACLE:
17592         e->nograb = 1;
17593         e->nograb_default = e->nograb;
17594         if(e->energy_state.health_current <= 0)
17595         {
17596             e->dead = 1;    // so it won't get hit
17597         }
17598         e->takedamage = obstacle_takedamage;//obstacle_takedamage;
17599         break;
17600     case TYPE_STEAMER:
17601         e->nograb = 1;
17602         e->nograb_default = e->nograb;
17603         e->think = steamer_think;
17604         e->base = e->position.y;
17605         break;
17606     case TYPE_TEXTBOX:    // New type for displaying text purposes
17607         e->nograb = 1;
17608         e->nograb_default = e->nograb;
17609         e->think = text_think;
17610         break;
17611     case TYPE_SHOT:
17612         e->energy_state.health_current = 1;
17613         e->nograb = 1;
17614         e->nograb_default = e->nograb;
17615         e->think = common_think;
17616         e->takedamage = arrow_takedamage;
17617         e->attacking = ATTACKING_ACTIVE;
17618         if(!e->model->speed && !e->modeldata.nomove)
17619         {
17620             e->modeldata.speed = 2;    // Set default speed to 2 for arrows
17621         }
17622         else if(e->modeldata.nomove)
17623         {
17624             e->modeldata.speed = 0;
17625         }
17626         if(e->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR)
17627         {
17628             e->base = 0;
17629         }
17630         else
17631         {
17632             e->base = e->position.y;
17633         }
17634         e->speedmul = 2;
17635         break;
17636     case TYPE_NONE:
17637         e->nograb = 1;
17638         e->nograb_default = e->nograb;
17639         if(e->modeldata.subject_to_gravity < 0)
17640         {
17641             e->modeldata.subject_to_gravity = 1;
17642         }
17643         //e->base=e->position.y; //complained?
17644         if(e->modeldata.no_adjust_base < 0)
17645         {
17646             e->modeldata.no_adjust_base = 1;
17647         }
17648 
17649         if(validanim(e, ANI_WALK))
17650         {
17651             if(e->direction == DIRECTION_RIGHT)
17652             {
17653                 e->velocity.x = e->modeldata.speed;
17654             }
17655             else
17656             {
17657                 e->velocity.x = -(e->modeldata.speed);
17658             }
17659             e->think = anything_walk;
17660 
17661             common_walk_anim(e);
17662             //ent_set_anim(e, ANI_WALK, 0);
17663         }
17664         else
17665         {
17666             e->speedmul = 2;
17667         }
17668         e->trymove = common_trymove;
17669         break;
17670     case TYPE_PANEL:
17671         e->nograb = 1;
17672         e->nograb_default = e->nograb;
17673         break;
17674     }
17675 
17676     if(!e->animation)
17677     {
17678         set_idle(e);
17679     }
17680 
17681     if(e->modeldata.multiple < 0)
17682     {
17683         e->modeldata.multiple = 0;
17684     }
17685 
17686     if(e->modeldata.subject_to_platform > 0 && (other = check_platform_below(e->position.x, e->position.z, e->position.y, e)))
17687     {
17688         e->base = other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT];
17689     }
17690     else if(e->modeldata.subject_to_wall > 0 && (wall = checkwall_below(e->position.x, e->position.z, T_MAX_CHECK_ALTITUDE)) >= 0)
17691     {
17692         if(level->walls[wall].height > MAX_WALL_HEIGHT)
17693         {
17694             e->position.x = find_nearest_wall_x(wall, e->position.x, e->position.z);
17695         }
17696         else
17697         {
17698             e->base = level->walls[wall].height;
17699         }
17700     }
17701 }
17702 
ent_spawn_ent(entity * ent)17703 void ent_spawn_ent(entity *ent)
17704 {
17705     entity *s_ent = NULL;
17706     float *spawnframe = ent->animation->spawnframe;
17707     float dy = level ? 4.0 : 0.0;
17708     // spawn point relative to current entity
17709     if(spawnframe[4] == 0)
17710     {
17711         s_ent = spawn(ent->position.x + ((ent->direction == DIRECTION_RIGHT) ? spawnframe[1] : -spawnframe[1]), ent->position.z + spawnframe[2], ent->position.y + spawnframe[3], ent->direction, NULL, ent->animation->subentity, NULL);
17712     }
17713     //relative to screen position
17714     else if(spawnframe[4] == 1)
17715     {
17716         if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN))
17717         {
17718             s_ent = spawn(advancex + spawnframe[1], advancey + spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17719         }
17720         else
17721         {
17722             s_ent = spawn(advancex + spawnframe[1], spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17723         }
17724     }
17725     //absolute position in level
17726     else
17727     {
17728         s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3] + 0.001, 0, NULL, ent->animation->subentity, NULL);
17729     }
17730 
17731     if(s_ent)
17732     {
17733         //ent_default_init(s_ent);
17734         if(s_ent->modeldata.type & TYPE_SHOT)
17735         {
17736             s_ent->playerindex = ent->playerindex;
17737         }
17738         if(s_ent->modeldata.subtype == SUBTYPE_ARROW)
17739         {
17740             s_ent->owner = ent;
17741         }
17742         s_ent->spawntype = SPAWN_TYPE_CMD_SPAWN;
17743         s_ent->parent = ent;  //maybe used by A.I.
17744         execute_onspawn_script(s_ent);
17745     }
17746 }
17747 
ent_summon_ent(entity * ent)17748 void ent_summon_ent(entity *ent)
17749 {
17750     entity *s_ent = NULL;
17751     float *spawnframe = ent->animation->summonframe;
17752     float dy = level ? 4.0 : 0.0;
17753     // spawn point relative to current entity
17754     if(spawnframe[4] == 0)
17755     {
17756         s_ent = spawn(ent->position.x + ((ent->direction == DIRECTION_RIGHT) ? spawnframe[1] : -spawnframe[1]), ent->position.z + spawnframe[2],  ent->position.y + spawnframe[3], ent->direction, NULL, ent->animation->subentity, NULL);
17757     }
17758     //relative to screen position
17759     else if(spawnframe[4] == 1)
17760     {
17761         if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN))
17762         {
17763             s_ent = spawn(advancex + spawnframe[1], advancey + spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17764         }
17765         else
17766         {
17767             s_ent = spawn(advancex + spawnframe[1], spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17768         }
17769     }
17770     //absolute position in level
17771     else
17772     {
17773         s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17774     }
17775 
17776     if(s_ent)
17777     {
17778         if(!spawnframe[4])
17779         {
17780             s_ent->direction = ent->direction;
17781         }
17782         //ent_default_init(s_ent);
17783         if(s_ent->modeldata.type & TYPE_SHOT)
17784         {
17785             s_ent->playerindex = ent->playerindex;
17786         }
17787         if(s_ent->modeldata.subtype == SUBTYPE_ARROW)
17788         {
17789             s_ent->owner = ent;
17790         }
17791         //maybe used by A.I.
17792         s_ent->spawntype = SPAWN_TYPE_CMD_SUMMON;
17793         s_ent->parent = ent;
17794         ent->subentity = s_ent;
17795         execute_onspawn_script(s_ent);
17796     }
17797 }
17798 
calculate_edelay(entity * ent,int f)17799 int calculate_edelay(entity *ent, int f)
17800 {
17801     int iDelay, iED_Mode, iED_Capmin, iED_CapMax, iED_RangeMin, iED_RangeMax;
17802     float fED_Factor;
17803     s_anim *anim = ent->animation;
17804 
17805     iDelay          = anim->delay[f];
17806     iED_Mode        = ent->modeldata.edelay.mode;
17807     fED_Factor      = ent->modeldata.edelay.factor;
17808     iED_Capmin      = ent->modeldata.edelay.cap.min;
17809     iED_CapMax      = ent->modeldata.edelay.cap.max;
17810     iED_RangeMin    = ent->modeldata.edelay.range.min;
17811     iED_RangeMax    = ent->modeldata.edelay.range.max;
17812 
17813     if (iDelay >= iED_RangeMin && iDelay <= iED_RangeMax) //Regular delay within ignore ranges?
17814     {
17815         switch(iED_Mode)
17816         {
17817         case EDELAY_MODE_MULTIPLY:
17818             iDelay = (int)(iDelay * fED_Factor);
17819             break;
17820         case EDELAY_MODE_ADD:
17821         default:
17822             iDelay += (int)fED_Factor;
17823             break;
17824         }
17825 
17826         if (iED_Capmin && iDelay < iED_Capmin)
17827         {
17828             iDelay = iED_Capmin;
17829         }
17830         if (iED_CapMax && iDelay > iED_CapMax)
17831         {
17832             iDelay = iED_CapMax;
17833         }
17834     }
17835     return iDelay;
17836 }
17837 
17838 // Caskey, Damon V.
17839 // 2018-04-20
17840 //
17841 // Set up jumping velocity for an animation
17842 // that has jump frame defined and is at the
17843 // designated jump frame. Also spawns effect
17844 // entity if one is defined.
check_jumpframe(entity * ent,unsigned int frame)17845 bool check_jumpframe(entity *ent, unsigned int frame)
17846 {
17847     entity *effect;
17848 
17849     // Must have jump frame allocated.
17850     if(!ent->animation->jumpframe)
17851     {
17852         return 0;
17853     }
17854 
17855     // Must be on assigned jump frame.
17856     if(ent->animation->jumpframe->frame != frame)
17857     {
17858         return 0;
17859     }
17860 
17861     // Chuck entity into the air.
17862     toss(ent, ent->animation->jumpframe->velocity.y);
17863 
17864     // Set left or right horizontal velocity depending on
17865     // current direction.
17866     if(ent->direction == DIRECTION_RIGHT)
17867     {
17868         ent->velocity.x = ent->animation->jumpframe->velocity.x;
17869     }
17870     else
17871     {
17872         ent->velocity.x = -ent->animation->jumpframe->velocity.x;
17873     }
17874 
17875     // Lateral velocity.
17876     ent->velocity.z = ent->animation->jumpframe->velocity.z;
17877 
17878     // Spawn an effect entity if defined.
17879     if(ent->animation->jumpframe->ent >= 0)
17880     {
17881         effect = spawn(ent->position.x, ent->position.z, ent->position.y, ent->direction, NULL, ent->animation->jumpframe->ent, NULL);
17882         if(effect)
17883         {
17884             effect->spawntype = SPAWN_TYPE_DUST_JUMP;
17885             effect->base = ent->position.y;
17886             effect->autokill |= AUTOKILL_ANIMATION_COMPLETE;
17887             execute_onspawn_script(effect);
17888         }
17889     }
17890 
17891     return 1;
17892 
17893 }
17894 
17895 // move here to prevent some duplicated code in ent_sent_anim and update_ents
update_frame(entity * ent,unsigned int f)17896 void update_frame(entity *ent, unsigned int f)
17897 {
17898     entity *tempself;
17899     s_collision_attack attack;
17900     s_axis_principal_float move;
17901     s_anim *anim = ent->animation;
17902 
17903     if(f >= anim->numframes) // prevent a crash with invalid frame index.
17904     {
17905         return;
17906     }
17907 
17908     //important!
17909     tempself = self;
17910     self = ent;
17911 
17912     self->animpos = f;
17913     //self->currentsprite = self->animation->sprite[f];
17914 
17915     if(self->animating)
17916     {
17917         self->nextanim = _time + calculate_edelay(self, f);
17918         self->pausetime = 0;
17919         execute_animation_script(self);
17920     }
17921 
17922     if(ent->animation != anim || ent->animpos != f)
17923     {
17924         goto uf_interrupted;
17925     }
17926 
17927     if(level && (anim->move[f]->axis.x || anim->move[f]->axis.z))
17928     {
17929         move.x = (float)(anim->move[f]->axis.x ? anim->move[f]->axis.x : 0);
17930         move.z = (float)(anim->move[f]->axis.z ? anim->move[f]->axis.z : 0);
17931         if(self->direction == DIRECTION_LEFT)
17932         {
17933             move.x = -move.x;
17934         }
17935         self->movex += move.x;
17936         self->movez += move.z;
17937     }
17938 
17939     if(anim->move[0]->base && anim->move[0]->base >= 0 && self->base <= 0)
17940     {
17941         ent->base = (float)anim->move[0]->base;
17942     }
17943     else if(!anim->move[0]->base || anim->move[0]->base < 0)
17944     {
17945         move.y = (float)(anim->move[f]->axis.y ? anim->move[f]->axis.y : 0);
17946         self->base += move.y;
17947         if(move.y != 0)
17948         {
17949             self->altbase += move.y;
17950         }
17951         else
17952         {
17953             self->altbase = 0;
17954         }
17955     }
17956 
17957     if(anim->flipframe == f)
17958     {
17959         self->direction = !self->direction;
17960     }
17961 
17962     if(anim->weaponframe && anim->weaponframe[0] == f)
17963     {
17964         dropweapon(2);
17965         set_weapon(self, anim->weaponframe[1], 0);
17966         if(!anim->weaponframe[2])
17967         {
17968             set_idle(self);
17969         }
17970     }
17971 
17972     if(anim->quakeframe.framestart + anim->quakeframe.cnt == f)
17973     {
17974         if(level)
17975         {
17976             if(anim->quakeframe.cnt % 2 || anim->quakeframe.v > 0)
17977             {
17978                 level->quake = anim->quakeframe.v;
17979             }
17980             else
17981             {
17982                 level->quake = anim->quakeframe.v * -1;
17983             }
17984         }
17985         if((anim->quakeframe.repeat - anim->quakeframe.cnt) > 1)
17986         {
17987             anim->quakeframe.cnt++;
17988         }
17989         else
17990         {
17991             anim->quakeframe.cnt = 0;
17992         }
17993     }
17994 
17995     if(anim->unsummonframe == f)
17996     {
17997         if(self->subentity)
17998         {
17999             self = self->subentity;
18000             attack = emptyattack;
18001             attack.dropv = default_model_dropv;
18002             attack.attack_force = self->energy_state.health_current;
18003             attack.attack_type = max_attack_types - 1;
18004             if(self->takedamage)
18005             {
18006                 self->takedamage(self, &attack, 0);
18007             }
18008             else
18009             {
18010                 kill_entity(self);
18011             }
18012             self = ent; // lol ...
18013             self->subentity = NULL;
18014         }
18015     }
18016 
18017     //spawn / summon /unsummon features
18018     if(anim->spawnframe && anim->spawnframe[0] == f && anim->subentity >= 0)
18019     {
18020         ent_spawn_ent(self);
18021     }
18022 
18023     if(anim->summonframe && anim->summonframe[0] == f && anim->subentity >= 0)
18024     {
18025         //subentity is dead
18026         if(!self->subentity || self->subentity->dead)
18027         {
18028             ent_summon_ent(self);
18029         }
18030     }
18031 
18032     if(anim->soundtoplay && anim->soundtoplay[f] >= 0)
18033     {
18034         sound_play_sample(anim->soundtoplay[f], 0, savedata.effectvol, savedata.effectvol, 100);
18035     }
18036 
18037     // Perform jumping if on a jumpframe.
18038     check_jumpframe(self, f);
18039 
18040 
18041     if(anim->projectile.throwframe == f)
18042     {
18043         // For backward compatible thing
18044         // throw stars in the air, hmm, strange
18045         // custstar custknife in animation should be checked first
18046         // then if the entity is jumping, check star first, if failed, try knife instead
18047         // well, try knife at last, if still failed, try star, or just let if shutdown?
18048 
18049         #define __trystar star_spawn(self->position.x + (self->direction == DIRECTION_RIGHT ? 56 : -56), self->position.z, self->position.y+67, self->direction)
18050         #define __tryknife knife_spawn(NULL, -1, self->position.x, self->position.z, self->position.y + anim->projectile.position.y, self->direction, 0, 0)
18051 
18052         if(anim->projectile.knife >= 0 || anim->projectile.flash >= 0)
18053         {
18054             __tryknife;
18055         }
18056         else if(anim->projectile.star >= 0)
18057         {
18058             __trystar;
18059         }
18060         else if(self->jumping)
18061         {
18062             if(!__trystar)
18063             {
18064                 __tryknife;
18065             }
18066         }
18067         else if(!__tryknife)
18068         {
18069             __trystar;
18070         }
18071         self->deduct_ammo = 1;
18072     }
18073 
18074     if(anim->projectile.shootframe == f)
18075     {
18076         knife_spawn(NULL, -1, self->position.x, self->position.z, self->position.y, self->direction, 1, 0);
18077         self->deduct_ammo = 1;
18078     }
18079 
18080     if(anim->projectile.tossframe == f)
18081     {
18082         bomb_spawn(NULL, -1, self->position.x, self->position.z, self->position.y + anim->projectile.position.y, self->direction, 0);
18083         self->deduct_ammo = 1;
18084     }
18085 
18086 uf_interrupted:
18087 
18088     //important!
18089     self = tempself;
18090 
18091     #undef __trystar
18092     #undef __tryknife
18093 }
18094 
18095 
ent_set_anim(entity * ent,int aninum,int resetable)18096 void ent_set_anim(entity *ent, int aninum, int resetable)
18097 {
18098     s_anim *ani = NULL;
18099     int animpos;
18100 
18101     if(!ent)
18102     {
18103         //printf("FATAL: tried to set animation with invalid address (no such object)");
18104         return;
18105     }
18106 
18107     if(aninum < 0 || aninum >= max_animations)
18108     {
18109         //printf("FATAL: tried to set animation with invalid index (%s, %i)", ent->name, aninum);
18110         return;
18111     }
18112 
18113     if(!validanim(ent, aninum))
18114     {
18115         //printf("FATAL: tried to set animation with invalid address (%s, %i)", ent->name, aninum);
18116         return;
18117     }
18118 
18119     ani = ent->modeldata.animation[aninum];
18120 
18121     if(!resetable && ent->animation == ani)
18122     {
18123         return;
18124     }
18125 
18126     if(ani->numframes == 0)
18127     {
18128         return;
18129     }
18130 
18131     if(ent->animation && ((resetable & 2) || (ani->sync >= 0 && ent->animation->sync == ani->sync)))
18132     {
18133         animpos = ent->animpos;
18134         if(animpos >= ani->numframes)
18135         {
18136             animpos = 0;
18137         }
18138         ent->animnum_previous = ent->animnum;
18139         ent->animnum = aninum;
18140         ent->animation = ani;
18141         ent->animpos = animpos;
18142         ent->walking = 0;
18143     }
18144     else
18145     {
18146         ent->animnum_previous = ent->animnum;
18147         ent->animnum = aninum;    // Stored for nocost usage
18148         ent->animation = ani;
18149         ent->animation->animhits = 0;
18150 
18151         ent->animating = ANIMATING_FORWARD;
18152         ent->lasthit = ent->grabbing;
18153         ent->altbase = 0;
18154         ent->walking = 0;
18155 
18156         update_frame(ent, 0);
18157     }
18158 }
18159 
model_get_colourmap(s_model * model,unsigned which)18160 unsigned char *model_get_colourmap(s_model *model, unsigned which)
18161 {
18162     if(which <= 0 || which > model->maps_loaded)
18163     {
18164         return model->palette;
18165     }
18166     else
18167     {
18168         return model->colourmap[which - 1];
18169     }
18170 }
18171 
18172 // 0 = none, 1+ = alternative
ent_set_colourmap(entity * ent,unsigned int which)18173 void ent_set_colourmap(entity *ent, unsigned int which)
18174 {
18175     if(which > ent->modeldata.maps_loaded)
18176     {
18177         which = 0;
18178     }
18179     if(which <= 0)
18180     {
18181         ent->colourmap = ent->modeldata.palette;
18182     }
18183     else
18184     {
18185         ent->colourmap = ent->modeldata.colourmap[which - 1];
18186     }
18187     ent->map = which;
18188 }
18189 
18190 // used by ent_set_model
ent_copy_uninit(entity * ent,s_model * oldmodel)18191 void ent_copy_uninit(entity *ent, s_model *oldmodel)
18192 {
18193     if(ent->modeldata.multiple < 0)
18194     {
18195         ent->modeldata.multiple             = oldmodel->multiple;
18196     }
18197     if(ent->modeldata.subject_to_basemap < 0)
18198     {
18199         ent->modeldata.subject_to_basemap   = oldmodel->subject_to_basemap;
18200     }
18201     if(ent->modeldata.subject_to_wall < 0)
18202     {
18203         ent->modeldata.subject_to_wall      = oldmodel->subject_to_wall;
18204     }
18205     if(ent->modeldata.subject_to_platform < 0)
18206     {
18207         ent->modeldata.subject_to_platform  = oldmodel->subject_to_platform;
18208     }
18209     if(ent->modeldata.subject_to_obstacle < 0)
18210     {
18211         ent->modeldata.subject_to_obstacle  = oldmodel->subject_to_obstacle;
18212     }
18213     if(ent->modeldata.subject_to_hole < 0)
18214     {
18215         ent->modeldata.subject_to_hole      = oldmodel->subject_to_hole;
18216     }
18217     if(ent->modeldata.subject_to_gravity < 0)
18218     {
18219         ent->modeldata.subject_to_gravity   = oldmodel->subject_to_gravity;
18220     }
18221     if(ent->modeldata.subject_to_screen < 0)
18222     {
18223         ent->modeldata.subject_to_screen    = oldmodel->subject_to_screen;
18224     }
18225     if(ent->modeldata.subject_to_minz < 0)
18226     {
18227         ent->modeldata.subject_to_minz      = oldmodel->subject_to_minz;
18228     }
18229     if(ent->modeldata.subject_to_maxz < 0)
18230     {
18231         ent->modeldata.subject_to_maxz      = oldmodel->subject_to_maxz;
18232     }
18233     if(ent->modeldata.no_adjust_base < 0)
18234     {
18235         ent->modeldata.no_adjust_base       = oldmodel->no_adjust_base;
18236     }
18237     if(ent->modeldata.aimove == -1)
18238     {
18239         ent->modeldata.aimove               = oldmodel->aimove;
18240     }
18241     if(ent->modeldata.aiattack == -1)
18242     {
18243         ent->modeldata.aiattack             = oldmodel->aiattack;
18244     }
18245     if(ent->modeldata.hostile < 0)
18246     {
18247         ent->modeldata.hostile              = oldmodel->hostile;
18248     }
18249     if(ent->modeldata.candamage < 0)
18250     {
18251         ent->modeldata.candamage            = oldmodel->candamage;
18252     }
18253     if(ent->modeldata.projectilehit < 0)
18254     {
18255         ent->modeldata.projectilehit        = oldmodel->projectilehit;
18256     }
18257     if(!ent->modeldata.health)
18258     {
18259         ent->modeldata.health               = oldmodel->health;
18260     }
18261     if(!ent->modeldata.mp)
18262     {
18263         ent->modeldata.mp                   = oldmodel->mp;
18264     }
18265     if(ent->modeldata.risetime.rise == -1)
18266     {
18267         ent->modeldata.risetime.rise          = oldmodel->risetime.rise;
18268     }
18269     /*
18270     if(!ent->modeldata.antigrab)
18271     	ent->modeldata.antigrab             = oldmodel->antigrab;
18272     if(!ent->modeldata.grabforce)
18273     	ent->modeldata.grabforce            = oldmodel->grabforce;
18274     if(!ent->modeldata.paingrab)
18275     	ent->modeldata.paingrab             = oldmodel->paingrab;*/
18276 
18277     if(ent->energy_state.health_current > ent->modeldata.health)
18278     {
18279         ent->energy_state.health_current = ent->modeldata.health;
18280     }
18281     if(ent->energy_state.mp_current > ent->modeldata.mp)
18282     {
18283         ent->energy_state.mp_current = ent->modeldata.mp;
18284     }
18285 }
18286 
18287 
18288 //if syncAnim is set, only change animation reference
ent_set_model(entity * ent,char * modelname,int syncAnim)18289 void ent_set_model(entity *ent, char *modelname, int syncAnim)
18290 {
18291     s_model *m = NULL;
18292     s_model oldmodel;
18293     if(ent == NULL)
18294     {
18295         borShutdown(1, "FATAL: tried to change model of invalid object");
18296     }
18297     m = findmodel(modelname);
18298     if(m == NULL)
18299     {
18300         borShutdown(1, "Model not found: '%s'", modelname);
18301     }
18302     oldmodel = ent->modeldata;
18303     ent->model = m;
18304     ent->modeldata = *m;
18305     ent_copy_uninit(ent, &oldmodel);
18306     ent_set_colourmap(ent, ent->map);
18307 
18308     if(syncAnim && m->animation[ent->animnum])
18309     {
18310         ent->animation = m->animation[ent->animnum];
18311         if(ent->animpos >= ent->animation->numframes)
18312         {
18313             ent->animpos = ent->animation->numframes - 1;
18314         }
18315         ent->nextanim = _time + calculate_edelay(ent, ent->animpos);
18316         //update_frame(ent, ent->animpos);
18317     }
18318     else
18319     {
18320         ent->attacking = ATTACKING_NONE;
18321 
18322         if((!selectScreen && !_time) || !(ent->modeldata.type & TYPE_PLAYER))
18323         {
18324             // use new playerselect spawn anim
18325             if( validanim(ent, ANI_SPAWN))
18326             {
18327                 ent_set_anim(ent, ANI_SPAWN, 0);
18328             }
18329             else
18330             {
18331                 if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0);
18332             }
18333         }
18334         else if(!selectScreen && _time && (ent->modeldata.type & TYPE_PLAYER))
18335         {
18336             // mid-level respawn
18337             if( validanim(ent, ANI_RESPAWN))
18338             {
18339                 ent_set_anim(ent, ANI_RESPAWN, 0);
18340             }
18341             else if( validanim(ent, ANI_SPAWN))
18342             {
18343                 ent_set_anim(ent, ANI_SPAWN, 0);
18344             }
18345             else
18346             {
18347                 if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0);
18348             }
18349         }
18350         else if(selectScreen && validanim(ent, ANI_SELECT))
18351         {
18352 			// Play transition if we have one. Default Select otherwise.
18353 			if (validanim(ent, ANI_SELECTIN))
18354 			{
18355 				ent_set_anim(ent, ANI_SELECTIN, 0);
18356 			}
18357 			else
18358 			{
18359 				ent_set_anim(ent, ANI_SELECT, 0);
18360 			}
18361         }
18362         else
18363         {
18364             if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0);
18365         }
18366     }
18367 }
18368 
allocate_drawmethod()18369 s_drawmethod *allocate_drawmethod()
18370 {
18371 	s_drawmethod *result;
18372 
18373 	// Allocate memory for new drawmethod structure and get pointer.
18374 	result = malloc(sizeof(*result));
18375 
18376 	// Copy default values into new drawmethod.
18377 	memcpy(result, &plainmethod, sizeof(*result));
18378 
18379 	return result;
18380 }
18381 
spawn(float x,float z,float a,e_direction direction,char * name,int index,s_model * model)18382 entity *spawn(float x, float z, float a, e_direction direction, char *name, int index, s_model *model)
18383 {
18384     entity *e = NULL;
18385     int i, id;
18386     s_defense *dfs;
18387     float *ofs;
18388     Varlist *vars;
18389     s_scripts *scripts;
18390 
18391     if(!model)
18392     {
18393         if(index >= 0)
18394         {
18395             model = model_cache[index].model;
18396         }
18397         else if(name)
18398         {
18399             model = findmodel(name);
18400         }
18401     }
18402 
18403     // Be a bit more tolerant...
18404     if(model == NULL)
18405     {
18406         /*
18407         if(index>=0)
18408         	printf("FATAL: attempt to spawn object with invalid model cache id (%d)!\n", index);
18409         else if(name)
18410         	printf("FATAL: attempt to spawn object with invalid model name (%s)!\n", name);*/
18411         return NULL;
18412     }
18413 
18414     if(ent_count >= ent_list_size && !alloc_ents())
18415     {
18416         return NULL;    //out of memory ?
18417     }
18418 
18419     for(i = 0; i < ent_list_size; i++)
18420     {
18421         if(!ent_list[i]->exists || (ent_count >= spawnoverride && ent_list[i]->modeldata.priority < 0 && ent_list[i]->modeldata.priority <= model->priority))
18422         {
18423             e = ent_list[i];
18424             if(e->exists)
18425             {
18426                 kill_entity(e);
18427             }
18428             // save these values, or they will loss when memset called
18429             id      = e->sortid;
18430             dfs     = e->defense;
18431             ofs     = e->offense_factors;
18432             vars    = e->varlist;
18433             Varlist_Cleanup(vars);
18434             memcpy(dfs, model->defense, sizeof(*dfs)*max_attack_types);
18435             memcpy(ofs, model->offense_factors, sizeof(*ofs)*max_attack_types);
18436             // clear up
18437             clear_all_scripts(e->scripts, 1);
18438             if(e->waypoints)
18439             {
18440                 free(e->waypoints);
18441             }
18442 
18443             scripts = e->scripts;
18444             memset(e, 0, sizeof(*e));
18445 
18446 			// e->drawmethod = plainmethod;
18447 			e->drawmethod = allocate_drawmethod();
18448 
18449             e->drawmethod->flag = 0;
18450 
18451             // add to list and count current entities
18452             e->exists = 1;
18453             ent_count++;
18454 
18455             e->modeldata = *model; // copy the entir model data here
18456             e->model = model;
18457             e->defaultmodel = model;
18458 
18459             e->scripts = scripts;
18460             // copy from model a fresh script
18461 
18462             copy_all_scripts(model->scripts, e->scripts, 1);
18463 
18464             if(ent_count > ent_max)
18465             {
18466                 ent_max = ent_count;
18467             }
18468             e->timestamp = _time; // log time so update function will ignore it if it is new
18469 
18470             e->energy_state.health_current = e->modeldata.health;
18471             e->energy_state.mp_current = e->modeldata.mp;
18472             e->knockdowncount = e->modeldata.knockdowncount;
18473             e->position.x = x;
18474             e->position.z = z;
18475             e->position.y = a;
18476             e->direction = direction;
18477             e->nextthink = _time + 1;
18478             e->nextmove = _time + 1;
18479             e->speedmul = 1;
18480             ent_set_colourmap(e, 0);
18481             e->lifespancountdown = model->lifespan; // new life span countdown
18482             if((e->modeldata.type & TYPE_PLAYER) && ((level && level->nohit == DAMAGE_FROM_PLAYER_OFF) || savedata.mode))
18483             {
18484                 e->modeldata.hostile &= ~TYPE_PLAYER;
18485                 e->modeldata.candamage &= ~TYPE_PLAYER;
18486             }
18487             if(e->modeldata.type & TYPE_PLAYER)
18488             {
18489                 e->playerindex = currentspawnplayer;
18490             }
18491 
18492             if(e->modeldata.type & TYPE_TEXTBOX)
18493             {
18494                 textbox = e;
18495             }
18496 
18497             strncpy(e->name, e->modeldata.name, MAX_NAME_LEN - 1);
18498             // copy back the value
18499             e->sortid = id;
18500             e->defense = dfs;
18501             e->offense_factors = ofs;
18502             e->varlist = vars;
18503             ent_default_init(e);
18504             return e;
18505         }
18506     }
18507     return NULL;
18508 }
18509 
18510 
18511 
18512 // Break the link an entity has with another one
ent_unlink(entity * e)18513 void ent_unlink(entity *e)
18514 {
18515     if(e->link)
18516     {
18517         e->link->link = NULL;
18518         e->link->grabbing = NULL;
18519     }
18520     e->link = NULL;
18521     e->grabbing = NULL;
18522 }
18523 
18524 
18525 
18526 // Link two entities together
ents_link(entity * e1,entity * e2)18527 void ents_link(entity *e1, entity *e2)
18528 {
18529     ent_unlink(e1);
18530     ent_unlink(e2);
18531     e1->grabbing = e2;    // Added for platform layering
18532     e1->link = e2;
18533     e2->link = e1;
18534 }
18535 
18536 
18537 
kill_entity(entity * victim)18538 void kill_entity(entity *victim)
18539 {
18540     int i = 0;
18541     s_collision_attack attack;
18542     entity *tempent = self;
18543 
18544     if(victim == NULL || !victim->exists)
18545     {
18546         return;
18547     }
18548 
18549     execute_onkill_script(victim);
18550 
18551     ent_unlink(victim);
18552     victim->weapent = NULL;
18553     victim->energy_state.health_current = 0;
18554     victim->exists = 0;
18555     ent_count--;
18556 
18557     //UT: caution, script function killentity calls this
18558     clear_all_scripts(victim->scripts, 1);
18559 
18560     if(victim->parent && victim->parent->subentity == victim)
18561     {
18562         victim->parent->subentity = NULL;
18563     }
18564     victim->parent = NULL;
18565     if(victim->modeldata.summonkill)
18566     {
18567         attack = emptyattack;
18568         attack.attack_type = max_attack_types - 1;
18569         attack.dropv = default_model_dropv;
18570     }
18571     // kill minions
18572     if(victim->modeldata.summonkill == 1 && victim->subentity)
18573     {
18574         // kill only summoned one
18575         victim->subentity->parent = NULL;
18576         self = victim->subentity;
18577         attack.attack_force = self->energy_state.health_current;
18578         if(self->takedamage && !level_completed)
18579         {
18580             self->takedamage(self, &attack, 0);
18581         }
18582         else
18583         {
18584             kill_entity(self);
18585         }
18586     }
18587     victim->subentity = NULL;
18588 
18589     for (i = 0; i < MAX_PLAYERS; i++) {
18590         if(victim == player[i].ent) {
18591             player[i].ent = NULL;
18592             break;
18593         }
18594     }
18595 
18596     if(victim == smartbomber)
18597     {
18598         smartbomber = NULL;
18599     }
18600     if(victim == textbox)
18601     {
18602         textbox = NULL;
18603     }
18604 
18605     for(i = 0; i < ent_max; i++)
18606     {
18607         if(ent_list[i]->exists)
18608         {
18609             // kill all minions
18610             self = ent_list[i];
18611             if(self->parent == victim)
18612             {
18613                 self->parent = NULL;
18614                 if(victim->modeldata.summonkill == 2)
18615                 {
18616                     attack.attack_force = self->energy_state.health_current;
18617                     if(self->takedamage && !level_completed)
18618                     {
18619                         self->takedamage(self, &attack, 0);
18620                     }
18621                     else
18622                     {
18623                         kill_entity(self);
18624                     }
18625                 }
18626             }
18627             if(self->owner == victim)
18628             {
18629                 self->owner = victim->owner;
18630             }
18631             if(self->opponent == victim)
18632             {
18633                 self->opponent = NULL;
18634             }
18635             if(self->binding.ent == victim)
18636             {
18637                 self->binding.ent = NULL;
18638             }
18639             if(self->landed_on_platform == victim)
18640             {
18641                 self->landed_on_platform = NULL;
18642             }
18643             if(self->hithead == victim)
18644             {
18645                 self->hithead = NULL;
18646             }
18647             if(self->lasthit == victim)
18648             {
18649                 self->lasthit = NULL;
18650             }
18651             if(!textbox && (self->modeldata.type & TYPE_TEXTBOX))
18652             {
18653                 textbox = self;
18654             }
18655         }
18656     }
18657 
18658     victim = NULL;
18659 
18660     //free_ent(victim);
18661     //victim = alloc_ent();
18662 
18663     self = tempent;
18664 }
18665 
18666 
kill_all()18667 void kill_all()
18668 {
18669     int i;
18670     entity *e = NULL;
18671     for(i = 0; i < ent_max; i++)
18672     {
18673         e = ent_list[i];
18674         if (e && e->exists)
18675         {
18676             execute_onkill_script(e);
18677             clear_all_scripts(e->scripts, 1);
18678         }
18679         e->exists = 0; // well, no need to use kill function
18680     }
18681     textbox = smartbomber = NULL;
18682     _time = 0;
18683     ent_count = ent_max = ent_stack_size = 0;
18684     if(ent_list_size > MAX_ENTS) //shrinking...
18685     {
18686         free_ents();
18687         alloc_ents(); //this shouldn't return 0, because the list shrinks...
18688     }
18689 }
18690 
checkhit(entity * attacker,entity * target)18691 int checkhit(entity *attacker, entity *target)
18692 {
18693     #define KEY_ATTACKER    0
18694     #define KEY_TARGET      1
18695     #define KEY_POS_X       0
18696     #define KEY_POS_Y       1
18697     #define KEY_SIZE_X      2
18698     #define KEY_SIZE_Y      3
18699 
18700     s_hitbox *coords_attack;
18701     s_hitbox *coords_detect;
18702     s_collision_attack  *attack = NULL;
18703     s_collision_body    *detect = NULL;
18704     int x1,
18705         x2,
18706         y1,
18707         y2,
18708         attack_instance;
18709     float medx,
18710         medy;
18711     int attack_pos_x    = 0,
18712         attack_pos_y    = 0,
18713         attack_size_x   = 0,
18714         attack_size_y   = 0,
18715         detect_pos_x    = 0,
18716         detect_pos_y    = 0,
18717         detect_size_x   = 0,
18718         detect_size_y   = 0;
18719     int topleast,
18720         bottomleast,
18721         leftleast,
18722         rightleast;
18723     float zdist = 0,
18724         z1 = 0,
18725         z2 = 0;
18726 
18727     if(attacker == target
18728        || !target->animation->collision_body
18729        || !attacker->animation->collision_attack
18730        || !target->animation->vulnerable[target->animpos]
18731        )
18732     {
18733         return 0;
18734     }
18735 
18736     int detect_instance = 0;
18737     int collision_found = 0;
18738 
18739     for(attack_instance = 0; attack_instance < max_collisons; attack_instance++)
18740     {
18741         attack          = attacker->animation->collision_attack[attacker->animpos]->instance[attack_instance];
18742         coords_attack   = attack->coords;
18743 
18744         for(detect_instance = 0; detect_instance < max_collisons; detect_instance++)
18745         {
18746             // Z calculations use increments in,
18747             // each loop, so we need to reset them here.
18748             z1      = attacker->position.z;
18749             z2      = target->position.z;
18750             zdist   = 0;
18751 
18752             //if(!attack->counterattack)
18753             //{
18754                 detect          = target->animation->collision_body[target->animpos]->instance[detect_instance];
18755                 coords_detect   = detect->coords;
18756 
18757             //}
18758             //else if((target->animation->collision_attack && target->animation->collision_attack[target->animpos]) && target->animation->collision_attack[target->animpos]->counterattack <= attacker->animation->collision_attack[attacker->animpos]->counterattack)
18759             //{
18760                 //coords_detect = target->animation->collision_attack[target->animpos]->coords;
18761             //}
18762             //else
18763             //{
18764             //    return 0;
18765             //}
18766 
18767             if(coords_attack->z2 > coords_attack->z1)
18768             {
18769                 z1 += coords_attack->z1 + (coords_attack->z2 - coords_attack->z1) / 2;
18770                 zdist = (coords_attack->z2 - coords_attack->z1) / 2;
18771             }
18772             else if(coords_attack->z1)
18773             {
18774                 zdist += coords_attack->z1;
18775             }
18776             else
18777             {
18778                 zdist += attacker->modeldata.grabdistance / 3 + 1;    //temporay fix for integer to float conversion
18779             }
18780 
18781             if(coords_detect->z2 > coords_detect->z1)
18782             {
18783                 z2 += coords_detect->z1 + (coords_detect->z2 - coords_detect->z1) / 2;
18784                 zdist += (coords_detect->z2 - coords_detect->z1) / 2;
18785             }
18786             else if(coords_detect->z1)
18787             {
18788                 zdist += coords_detect->z1;
18789             }
18790 
18791             zdist++; // pass >= <= check
18792 
18793             if(diff(z1, z2) > zdist)
18794             {
18795                 continue;
18796             }
18797 
18798             x1 = (int)(attacker->position.x);
18799             y1 = (int)(z1 - attacker->position.y);
18800             x2 = (int)(target->position.x);
18801             y2 = (int)(z2 - target->position.y);
18802 
18803             if(attacker->direction == DIRECTION_LEFT)
18804             {
18805                 attack_pos_x   = x1 - coords_attack->width;
18806                 attack_pos_y   = y1 + coords_attack->y;
18807                 attack_size_x  = x1 - coords_attack->x;
18808                 attack_size_y  = y1 + coords_attack->height;
18809             }
18810             else
18811             {
18812                 attack_pos_x    = x1 + coords_attack->x;
18813                 attack_pos_y    = y1 + coords_attack->y;
18814                 attack_size_x   = x1 + coords_attack->width;
18815                 attack_size_y   = y1 + coords_attack->height;
18816             }
18817 
18818             if(target->direction == DIRECTION_LEFT)
18819             {
18820                 detect_pos_x    = x2 - coords_detect->width;
18821                 detect_pos_y    = y2 + coords_detect->y;
18822                 detect_size_x   = x2 - coords_detect->x;
18823                 detect_size_y   = y2 + coords_detect->height;
18824             }
18825             else
18826             {
18827                 detect_pos_x    = x2 + coords_detect->x;
18828                 detect_pos_y    = y2 + coords_detect->y;
18829                 detect_size_x   = x2 + coords_detect->width;
18830                 detect_size_y   = y2 + coords_detect->height;
18831             }
18832 
18833             if(attack_pos_x > detect_size_x)
18834             {
18835                 continue;
18836             }
18837             if(detect_pos_x > attack_size_x)
18838             {
18839                 continue;
18840             }
18841             if(attack_pos_y > detect_size_y)
18842             {
18843                 continue;
18844             }
18845             if(detect_pos_y > attack_size_y)
18846             {
18847                 continue;
18848             }
18849 
18850             // If we got this far, set collision flag
18851             // and break this loop.
18852             collision_found = 1;
18853             break;
18854         }
18855 
18856         // If a collision was found
18857         // break out of loop.
18858         if(collision_found)
18859         {
18860             break;
18861         }
18862     }
18863 
18864     if(!collision_found)
18865     {
18866         return 0;
18867     }
18868 
18869     // Find center of attack area
18870     leftleast = attack_pos_x;
18871 
18872     if(leftleast < detect_pos_x)
18873     {
18874         leftleast = detect_pos_x;
18875     }
18876 
18877     topleast = attack_pos_y;
18878 
18879     if(topleast < detect_pos_y)
18880     {
18881         topleast = detect_pos_y;
18882     }
18883 
18884     rightleast = attack_size_x;
18885 
18886     if(rightleast > detect_size_x)
18887     {
18888         rightleast = detect_size_x;
18889     }
18890 
18891     bottomleast = attack_size_y;
18892 
18893     if(bottomleast > detect_size_y)
18894     {
18895         bottomleast = detect_size_y;
18896     }
18897 
18898     medx = (float)(leftleast + rightleast) / 2;
18899     medy = (float)(topleast + bottomleast) / 2;
18900 
18901     // Now convert these coords to 3D
18902     lasthit.position.x = medx;
18903 
18904     if(attacker->position.z > target->position.z)
18905     {
18906         lasthit.position.z = z1 + 1;    // Changed so flashes always spawn in front
18907     }
18908     else
18909     {
18910         lasthit.position.z = z2 + 1;
18911     }
18912 
18913     lasthit.attack      = attack;
18914     lasthit.body        = detect;
18915     lasthit.position.y  = lasthit.position.z - medy;
18916 	lasthit.target = target;
18917 	lasthit.attacker = attacker;
18918     lasthit.confirm     = 1;
18919 
18920     return 1;
18921 
18922     #undef KEY_ATTACKER
18923     #undef KEY_TARGET
18924     #undef KEY_POS_X
18925     #undef KEY_POS_Y
18926     #undef KEY_SIZE_X
18927     #undef KEY_SIZE_Y
18928 }
18929 
18930 
18931 
18932 /*
18933 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
18934 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
18935 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
18936 within the bottom/top and the left/right area.
18937 */
testhole(int hole,float x,float z)18938 int testhole(int hole, float x, float z)
18939 {
18940     float coef1, coef2;
18941     if(z <= level->holes[hole].z && z >= level->holes[hole].z - level->holes[hole].depth)
18942     {
18943         coef1 = (level->holes[hole].z - z) * ((level->holes[hole].upperleft - level->holes[hole].lowerleft) / level->holes[hole].depth);
18944         coef2 = (level->holes[hole].z - z) * ((level->holes[hole].upperright - level->holes[hole].lowerright) / level->holes[hole].depth);
18945         if(x >= level->holes[hole].x + level->holes[hole].lowerleft + coef1 && x <= level->holes[hole].x + level->holes[hole].lowerright + coef2)
18946         {
18947             return 1;
18948         }
18949     }
18950     return 0;
18951 }
18952 
18953 // find all holes here and return the count
checkholes(float x,float z)18954 int checkholes(float x, float z)
18955 {
18956     int i, c;
18957 
18958     for(i = 0, c = 0; i < level->numholes; i++)
18959     {
18960         c += testhole(i, x, z);
18961     }
18962 
18963     return c;
18964 }
18965 
18966 // find the 1st hole here
checkhole(float x,float z)18967 int checkhole(float x, float z)
18968 {
18969     int i;
18970 
18971     if(level == NULL)
18972     {
18973         return 0;
18974     }
18975 
18976     for(i = 0; i < level->numholes; i++)
18977     {
18978         if(testhole(i, x, z))
18979         {
18980             holez = i;
18981             return 1;
18982         }
18983     }
18984     return 0;
18985 }
18986 
18987 // find all holes here within altitude1 and 2, return the count
checkhole_between(float x,float z,float a1,float a2)18988 int checkhole_between(float x, float z, float a1, float a2)
18989 {
18990     int i, c;
18991 
18992     for(i = 0, c = 0; i < level->numholes; i++)
18993     {
18994         c += (testhole(i, x, z) && level->holes[i].height >= a1 && level->holes[i].height <= a2);
18995     }
18996 
18997     return c;
18998 }
18999 
19000 // get a highest hole below this altitude
checkhole_in(float x,float z,float a)19001 int checkhole_in(float x, float z, float a)
19002 {
19003     float maxa;
19004     int i, ind;
19005 
19006     if(level == NULL)
19007     {
19008         return 0;
19009     }
19010 
19011     maxa = -1;
19012     ind = -1;
19013     for(i = 0; i < level->numholes; i++)
19014     {
19015         if(testhole(i, x, z) && level->holes[i].height+T_WALKOFF >= a && level->holes[i].height > maxa) // && level->holes[i].height+T_WALKOFF >= a
19016         {
19017             maxa = level->holes[i].height;
19018             ind = i;
19019         }
19020     }
19021 
19022     if ( ind >= 0 ) {
19023         holez = ind;
19024         return 1;
19025     } else return 0;
19026 }
19027 
19028 // find the hole id for highest hole
checkholeindex_in(float x,float z,float a)19029 int checkholeindex_in(float x, float z, float a)
19030 {
19031     float maxa;
19032     int i, ind;
19033 
19034     if(level == NULL)
19035     {
19036         return -1;
19037     }
19038 
19039     maxa = -1;
19040     ind = -1;
19041     for(i = 0; i < level->numholes; i++)
19042     {
19043         if(testhole(i, x, z) && level->holes[i].height+T_WALKOFF >= a && level->holes[i].height > maxa) // && level->holes[i].height+T_WALKOFF >= a
19044         {
19045             maxa = level->holes[i].height;
19046             ind = i;
19047         }
19048     }
19049 
19050     return ind;
19051 }
19052 
19053 // find the 1st hole id here
checkhole_index(float x,float z)19054 int checkhole_index(float x, float z)
19055 {
19056     int i;
19057 
19058     if(level == NULL)
19059     {
19060         return -1;
19061     }
19062 
19063     for(i = 0; i < level->numholes; i++)
19064     {
19065         if(testhole(i, x, z))
19066         {
19067             //holez = i;
19068             return i;
19069         }
19070     }
19071     return -1;
19072 }
19073 
19074 /*
19075 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
19076 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
19077 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
19078 within the bottom/top and the left/right area.
19079 */
testwall(int wall,float x,float z)19080 int testwall(int wall, float x, float z)
19081 {
19082     float coef1, coef2;
19083 
19084     //if(wall >= level->numwalls || wall < 0) return 0;
19085     if(z <= level->walls[wall].z && z >= level->walls[wall].z - level->walls[wall].depth)
19086     {
19087         coef1 = (level->walls[wall].z - z) * ((level->walls[wall].upperleft - level->walls[wall].lowerleft) / level->walls[wall].depth);
19088         coef2 = (level->walls[wall].z - z) * ((level->walls[wall].upperright - level->walls[wall].lowerright) / level->walls[wall].depth);
19089         if(x >= level->walls[wall].x + level->walls[wall].lowerleft + coef1 && x <= level->walls[wall].x + level->walls[wall].lowerright + coef2)
19090         {
19091             return 1;
19092         }
19093     }
19094 
19095     return 0;
19096 }
19097 
19098 // find all walls here within altitude1 and 2, return the count
checkwalls(float x,float z,float a1,float a2)19099 int checkwalls(float x, float z, float a1, float a2)
19100 {
19101     int i, c;
19102 
19103     for(i = 0, c = 0; i < level->numwalls; i++)
19104     {
19105         c += (testwall(i, x, z) && level->walls[i].height >= a1 && level->walls[i].height <= a2);
19106     }
19107 
19108     return c;
19109 }
19110 
19111 // get a highest wall below this altitude
checkwall_below(float x,float z,float a)19112 int checkwall_below(float x, float z, float a)
19113 {
19114     float maxa;
19115     int i, ind;
19116 
19117     if(level == NULL)
19118     {
19119         return -1;
19120     }
19121 
19122     maxa = 0;
19123     ind = -1;
19124     for(i = 0; i < level->numwalls; i++)
19125     {
19126         if(testwall(i, x, z) && level->walls[i].height <= a && level->walls[i].height > maxa)
19127         {
19128             maxa = level->walls[i].height;
19129             ind = i;
19130         }
19131     }
19132 
19133     return ind;
19134 }
19135 
19136 // return the 1st wall found here
checkwall_index(float x,float z)19137 int checkwall_index(float x, float z)
19138 {
19139     int i;
19140     if(level == NULL)
19141     {
19142         return -1;
19143     }
19144 
19145     for(i = 0; i < level->numwalls; i++)
19146         if(testwall(i, x, z))
19147         {
19148             return i;
19149         }
19150 
19151     return -1;
19152 }
19153 
19154 /*
19155 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
19156 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
19157 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
19158 within the bottom/top and the left/right area.
19159 */
testplatform(entity * plat,float x,float z,entity * exclude)19160 int testplatform(entity *plat, float x, float z, entity *exclude)
19161 {
19162     float coef1, coef2;
19163     float offz, offx;
19164     if(plat == exclude)
19165     {
19166         return 0;
19167     }
19168     if(!plat->animation || !plat->animation->platform || !plat->animation->platform[plat->animpos][PLATFORM_HEIGHT])
19169     {
19170         return 0;
19171     }
19172     offz = plat->position.z + plat->animation->platform[plat->animpos][PLATFORM_Z];
19173     offx = plat->position.x + plat->animation->platform[plat->animpos][PLATFORM_X];
19174     if(z <= offz && z >= offz - plat->animation->platform[plat->animpos][PLATFORM_DEPTH])
19175     {
19176         coef1 = (offz - z) * ((plat->animation->platform[plat->animpos][PLATFORM_UPPERLEFT] -
19177                                plat->animation->platform[plat->animpos][PLATFORM_LOWERLEFT]) / plat->animation->platform[plat->animpos][PLATFORM_DEPTH]);
19178         coef2 = (offz - z) * ((plat->animation->platform[plat->animpos][PLATFORM_UPPERRIGHT] -
19179                                plat->animation->platform[plat->animpos][PLATFORM_LOWERRIGHT]) / plat->animation->platform[plat->animpos][PLATFORM_DEPTH]);
19180 
19181         if(x >= offx + plat->animation->platform[plat->animpos][PLATFORM_LOWERLEFT] + coef1 &&
19182                 x <= offx + plat->animation->platform[plat->animpos][PLATFORM_LOWERRIGHT] + coef2)
19183         {
19184             return 1;
19185         }
19186     }
19187     return 0;
19188 }
19189 
19190 //find the first platform between these 2 altitudes
check_platform_between(float x,float z,float amin,float amax,entity * exclude)19191 entity *check_platform_between(float x, float z, float amin, float amax, entity *exclude)
19192 {
19193     entity *plat = NULL;
19194     int i;
19195 
19196     if(level == NULL)
19197     {
19198         return NULL;
19199     }
19200 
19201     for(i = 0; i < ent_max; i++)
19202     {
19203         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) )
19204         {
19205             plat = ent_list[i];
19206             if(plat->position.y <= amax && plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] > amin)
19207             {
19208                 return plat;
19209             }
19210         }
19211     }
19212     return NULL;
19213 }
19214 
19215 //find a lowest platform above this altitude
check_platform_above(float x,float z,float a,entity * exclude)19216 entity *check_platform_above(float x, float z, float a, entity *exclude)
19217 {
19218     float mina;
19219     entity *plat = NULL;
19220     int i, ind;
19221 
19222     if(level == NULL)
19223     {
19224         return NULL;
19225     }
19226 
19227     mina = 9999999;
19228     ind = -1;
19229     for(i = 0; i < ent_max; i++)
19230     {
19231         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) )
19232         {
19233             plat = ent_list[i];
19234             if(plat->position.y >= a && plat->position.y < mina)
19235             {
19236                 mina = plat->position.y;
19237                 ind = i;
19238             }
19239         }
19240     }
19241     return (ind >= 0) ? ent_list[ind] : NULL;
19242 }
19243 
19244 //find a highest platform below this altitude
check_platform_below(float x,float z,float a,entity * exclude)19245 entity *check_platform_below(float x, float z, float a, entity *exclude)
19246 {
19247     float maxa;
19248     entity *plat = NULL;
19249     int i, ind;
19250 
19251     if(level == NULL)
19252     {
19253         return NULL;
19254     }
19255 
19256     maxa = MIN_INT;
19257     ind = -1;
19258     for(i = 0; i < ent_max; i++)
19259     {
19260         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) )
19261         {
19262             plat = ent_list[i];
19263             if(plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] <= a &&
19264                     plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] > maxa)
19265             {
19266                 maxa = plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT];
19267                 ind = i;
19268             }
19269         }
19270     }
19271     return (ind >= 0) ? ent_list[ind] : NULL;
19272 }
19273 
check_platform_above_entity(entity * e)19274 entity *check_platform_above_entity(entity *e)
19275 {
19276     float mina;
19277     int heightvar;
19278     entity *plat = NULL;
19279     int i, ind;
19280 
19281     if(level == NULL)
19282     {
19283         return NULL;
19284     }
19285     if(e->animation->size.y)
19286     {
19287         heightvar = e->animation->size.y;
19288     }
19289     else
19290     {
19291         heightvar = e->modeldata.size.y;
19292     }
19293 
19294     mina = T_MAX_CHECK_ALTITUDE;
19295     ind = -1;
19296     for(i = 0; i < ent_max; i++)
19297     {
19298         if(ent_list[i]->exists && testplatform(ent_list[i], e->position.x, e->position.z, e) )
19299         {
19300             plat = ent_list[i];
19301             if(plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] > e->position.y + heightvar && plat->position.y < mina)
19302             {
19303                 mina = plat->position.y;
19304                 ind = i;
19305             }
19306         }
19307     }
19308     return (ind >= 0) ? ent_list[ind] : NULL;
19309 }
19310 
check_platform_below_entity(entity * e)19311 entity *check_platform_below_entity(entity *e)
19312 {
19313     float maxa, a;
19314     entity *plat = NULL;
19315     int i, ind;
19316 
19317     if(level == NULL)
19318     {
19319         return NULL;
19320     }
19321 
19322     maxa = MIN_INT;
19323     ind = -1;
19324     for(i = 0; i < ent_max; i++)
19325     {
19326         if(ent_list[i]->exists && testplatform(ent_list[i], e->position.x, e->position.z, e) )
19327         {
19328             plat = ent_list[i];
19329             a = plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT];
19330             if(a < e->position.y + T_WALKOFF && plat->position.y < e->position.y && a > maxa)
19331             {
19332                 maxa = a;
19333                 ind = i;
19334             }
19335         }
19336     }
19337     return (ind >= 0) ? ent_list[ind] : NULL;
19338 }
19339 
checkbase(float x,float z,float y,entity * ent)19340 float checkbase(float x, float z, float y, entity *ent)
19341 {
19342     float maxbase = -1.0f, base = -1.0f;
19343     int index = -1;
19344     entity *platform = NULL;
19345 
19346     base = (checkhole(self->position.x, self->position.z)) ? -1.0f : 0.0f;
19347     if (base > maxbase) maxbase = base;
19348 
19349     base = check_basemap(self->position.x, self->position.z);
19350     if (base > maxbase) maxbase = base;
19351 
19352     base = -1.0f;
19353     if((index = checkwall_below(x, z, T_MAX_CHECK_ALTITUDE)) >= 0)
19354     {
19355         base = level->walls[index].height;
19356         if (base > maxbase) maxbase = base;
19357     }
19358 
19359     base = -1.0f;
19360     platform = check_platform_below(x, z, y, ent);
19361     if (platform != NULL)
19362     {
19363         base = get_platform_base(platform);
19364         if (base > maxbase) maxbase = base;
19365     }
19366 
19367     return maxbase;
19368 }
19369 
19370 // find the 1st platform entity here
check_platform(float x,float z,entity * exclude)19371 entity *check_platform(float x, float z, entity *exclude)
19372 {
19373     int i;
19374     if(level == NULL)
19375     {
19376         return NULL;
19377     }
19378 
19379     for(i = 0; i < ent_max; i++)
19380     {
19381         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude))
19382         {
19383             return ent_list[i];
19384         }
19385     }
19386     return NULL;
19387 }
19388 
get_platform_base(entity * plat)19389 float get_platform_base(entity *plat)
19390 {
19391     float alt = 0;
19392 
19393     alt = plat->position.y;
19394     alt += plat->animation->platform[plat->animpos][PLATFORM_HEIGHT];
19395 
19396     return alt;
19397 }
19398 
is_on_platform(entity * ent)19399 int is_on_platform(entity *ent)
19400 {
19401     entity *plat = check_platform_below(ent->position.x,ent->position.z,ent->position.y+T_WALKOFF,ent);
19402     if (plat)
19403     {
19404         if ( diff(ent->base,get_platform_base(plat)) <= T_WALKOFF ) return 1;
19405     }
19406 
19407     return 0;
19408 }
19409 
get_platform_on(entity * ent)19410 entity *get_platform_on(entity *ent)
19411 {
19412     entity *plat = check_platform_below(ent->position.x,ent->position.z,ent->position.y+T_WALKOFF,ent);
19413     if (plat)
19414     {
19415         if ( diff(ent->base,get_platform_base(plat)) <= T_WALKOFF ) return plat;
19416     }
19417 
19418     return NULL;
19419 }
19420 
19421 // for adjust grab position function, test if an entity can move from a to b
19422 // TODO: check points between the two pionts, if necessary
19423 // special return values:
19424 // -1: ent can jump over walls, for jump check
19425 // -2: for hole ai reaction check
testmove(entity * ent,float sx,float sz,float x,float z)19426 int testmove(entity *ent, float sx, float sz, float x, float z)
19427 {
19428     entity *other = NULL, *platbelow = NULL;
19429     int wall, heightvar;
19430     float xdir, zdir;
19431 
19432     xdir = x - sx;
19433     zdir = z - sz;
19434 
19435     if(!xdir && !zdir)
19436     {
19437         return 1;
19438     }
19439 
19440     // -----------bounds checking---------------
19441     // Subjec to Z and out of bounds? Return to level!
19442     if (ent->modeldata.subject_to_minz > 0)
19443     {
19444         if(zdir && z < PLAYER_MIN_Z)
19445         {
19446             return 0;
19447         }
19448     }
19449 
19450     if (ent->modeldata.subject_to_maxz > 0)
19451     {
19452         if(zdir && z > PLAYER_MAX_Z)
19453         {
19454             return 0;
19455         }
19456     }
19457 
19458     // screen checking
19459     // Kratus (29-04-21) Reduced the "screen checking" range from 10 to 5 to avoid the entities to stuck in the edge of the screen
19460     // This change was made because the "common_trymove" function also has another "screen checking" with a range of 10 too
19461     // If the "testmove" function has a equal or bigger range than the "common_trymove" function, sometimes the entities will stuck
19462     if(ent->modeldata.subject_to_screen > 0)
19463     {
19464         if(x < advancex + 5)
19465         {
19466             return 0;
19467         }
19468         else if(x > advancex + (videomodes.hRes - 5))
19469         {
19470             return 0;
19471         }
19472     }
19473     //-----------end of bounds checking-----------
19474 
19475     //-------------hole checking ---------------------
19476     if(ent->modeldata.subject_to_hole > 0)
19477     {
19478         if(checkhole(x, z) && checkwall_index(x, z) < 0 && !check_platform_below(x, z, ent->position.y, ent))
19479         {
19480             return -2;
19481         }
19482     }
19483     //-----------end of hole checking---------------
19484 
19485     //--------------obstacle checking ------------------
19486     if(ent->modeldata.subject_to_obstacle > 0)
19487     {
19488         if((other = find_ent_here(ent, x, z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) &&
19489                 (!other->animation->platform || !other->animation->platform[other->animpos][PLATFORM_HEIGHT]))
19490         {
19491             return 0;
19492         }
19493     }
19494     //-----------end of obstacle checking--------------
19495 
19496     // ---------------- platform checking----------------
19497 
19498     if(ent->animation->size.y)
19499     {
19500         heightvar = ent->animation->size.y;
19501     }
19502     else
19503     {
19504         heightvar = ent->modeldata.size.y;
19505     }
19506 
19507     // Check for obstacles with platform code and adjust base accordingly
19508     if(ent->modeldata.subject_to_platform > 0 && (other = check_platform_between(x, z, ent->position.y, ent->position.y + heightvar, ent)) )
19509     {
19510         platbelow = check_platform_below(x, z, ent->position.y+T_WALKOFF, ent);
19511         if ( !platbelow ) return 0;
19512         else
19513         {
19514             float palt = get_platform_base(platbelow);
19515             if ( other != platbelow && diff(ent->position.y,palt) > T_WALKOFF ) return 0;
19516         }
19517     }
19518     //-----------end of platform checking------------------
19519 
19520     // ------------------ wall checking ---------------------
19521     if(ent->modeldata.subject_to_wall > 0 && (wall = checkwall_below(x, z, T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[wall].height > ent->position.y)
19522     {
19523         if(validanim(ent, ANI_JUMP) && sz < level->walls[wall].z && sz > level->walls[wall].z - level->walls[wall].depth) //Can jump?
19524         {
19525             //rmin = (float)ent->modeldata.animation[ANI_JUMP]->range.x.min;
19526             //rmax = (float)ent->modeldata.animation[ANI_JUMP]->range.x.max;
19527             if(level->walls[wall].height < ent->position.y + ent->modeldata.animation[ANI_JUMP]->range.x.max)
19528             {
19529                 return -1;
19530             }
19531         }
19532         return 0;
19533     }
19534     //----------------end of wall checking--------------
19535 
19536     return 1;
19537 
19538 }
19539 
19540 // find real opponent
set_opponent(entity * ent,entity * other)19541 void set_opponent(entity *ent, entity *other)
19542 {
19543     entity *realself, *realother;
19544 
19545     if(!ent)
19546     {
19547         return;
19548     }
19549 
19550     realself = ent;
19551     while(realself->owner)
19552     {
19553         realself = realself->owner;
19554     }
19555 
19556     realother = other;
19557     while(realother && realother->owner)
19558     {
19559         realother = realother->owner;
19560     }
19561 
19562     realself->opponent = ent->opponent = realother;
19563     if(realother)
19564     {
19565         realother->opponent = other->opponent = realself;
19566     }
19567 
19568 }
19569 
19570 // Caskey, Damon V.
19571 // 2018-12-31
19572 //
19573 // Initialize appropriate block animation and flags. Called when
19574 // entity blocks actively (blocking before attack hits). Used
19575 // by all player controlled entities or AI controlled entities
19576 // with nopassiveblock enabled.
do_active_block(entity * ent)19577 void do_active_block(entity *ent)
19578 {
19579 	// Run blocking action.
19580 	ent->takeaction = common_block;
19581 
19582 	// Stop movement.
19583 	ent->velocity.x = 0;
19584 	ent->velocity.z = 0;
19585 
19586 	// Set flags.
19587 	set_blocking(self);
19588 
19589 	// End combo.
19590 	self->combostep[0] = 0;
19591 
19592 	// If we have a block tranisiton animation, use it. Otherwise
19593 	// go right to block.
19594 	if (validanim(self, ANI_BLOCKSTART))
19595 	{
19596 		ent_set_anim(self, ANI_BLOCKSTART, 0);
19597 	}
19598 	else
19599 	{
19600 		ent_set_anim(self, ANI_BLOCK, 0);
19601 	}
19602 }
19603 
19604 // Caskey, Damon V.
19605 // 2018-09-16
19606 //
19607 // Find out if attack can be blocked by entity.
19608 // This function is concerned with the attack
19609 // vs. entity in terms of game mechanics like
19610 // guard break, attack type vs. defense, and
19611 // so on. It does not handle rules for AI blocking.
check_blocking_eligible(entity * ent,entity * other,s_collision_attack * attack)19612 int check_blocking_eligible(entity *ent, entity *other, s_collision_attack *attack)
19613 {
19614 	// If guardpoints are set, then find out if they've been depleted.
19615 	if (ent->modeldata.guardpoints.max)
19616 	{
19617 		if (ent->modeldata.guardpoints.current <= 0)
19618 		{
19619 			return 0;
19620 		}
19621 	}
19622 
19623 	// Attack block breaking exceeds block power?
19624 	if (attack->no_block || ent->defense[attack->attack_type].blockpower)
19625 	{
19626 		if (attack->no_block >= ent->defense[attack->attack_type].blockpower)
19627 		{
19628 			return 0;
19629 		}
19630 	}
19631 
19632 	// Attack from behind? Can't block that if
19633 	// we don't have blockback flag enabled.
19634 	if (ent->direction == other->direction)
19635 	{
19636 		if (!ent->modeldata.blockback)
19637 		{
19638 			return 0;
19639 		}
19640 	}
19641 
19642 	// Is there a blocking threshold? Verify it vs. attack force.
19643 	if (ent->modeldata.thold)
19644 	{
19645 		// Threshold value vs. attack.
19646 		if (attack->attack_force >= ent->modeldata.thold)
19647 		{
19648 			return 0;
19649 		}
19650 	}
19651 
19652 	// Is there a blocking threshhold for the attack type?
19653 	// Verify it vs. attack force.
19654 	if (ent->defense[attack->attack_type].blockthreshold)
19655 	{
19656 		if (ent->defense[attack->attack_type].blockthreshold > attack->attack_force)
19657 		{
19658 			return 0;
19659 		}
19660 	}
19661 
19662 	// If we made it through all that, then
19663 	// attack can be blocked. Return true.
19664 	return 1;
19665 }
19666 
19667 // Caskey, Damon V.
19668 // 2018-09-19
19669 //
19670 // Mandatory conditions the AI must pass before it
19671 // can decide to block. These are not rules for
19672 // blocking in general.
check_blocking_rules(entity * ent)19673 int check_blocking_rules(entity *ent)
19674 {
19675 	// If already blocking we can
19676 	// forget the rest and return
19677 	// true right away.
19678 	if (ent->blocking)
19679 	{
19680 		return 1;
19681 	}
19682 
19683 	// No blocking animation?
19684 	if (!validanim(ent, ANI_BLOCK))
19685 	{
19686 		return 0;
19687 	}
19688 
19689 	// Have to be idle.
19690 	if (!ent->idling)
19691 	{
19692 		return 0;
19693 	}
19694 
19695 	// AI can't be attacking.
19696 	if (ent->attacking == ATTACKING_ACTIVE)
19697 	{
19698 		return 0;
19699 	}
19700 
19701 	// Grappling?
19702 	if (ent->link)
19703 	{
19704 		return 0;
19705 	}
19706 
19707 	//  Airborne?
19708 	if (inair(ent))
19709 	{
19710 		return 0;
19711 	}
19712 
19713 	// Frozen?
19714 	if (ent->frozen)
19715 	{
19716 		return 0;
19717 	}
19718 
19719 	// Falling?
19720 	if (ent->falling)
19721 	{
19722 		return 0;
19723 	}
19724 
19725 	return 1;
19726 }
19727 
19728 // Caskey, Damon V.
19729 // 2018-09-17
19730 //
19731 // AI blocking decision. Handles AI's chances
19732 // to block. Returns true if AI chooses to attempt
19733 // a block.
check_blocking_decision(entity * ent)19734 int check_blocking_decision(entity *ent)
19735 {
19736 	// If we have nopassiveblock enabled and we're
19737 	// already blocking, then we want the AI to
19738 	// keep blocking (like most players would).
19739 	if (ent->modeldata.nopassiveblock)
19740 	{
19741 		if (ent->blocking)
19742 		{
19743 			return 1;
19744 		}
19745 	}
19746 
19747 	// Run random chance against blockodds. If it
19748 	// passes, AI will block.
19749 	if ((rand32()&ent->modeldata.blockodds) == 1)
19750 	{
19751 		return 1;
19752 	}
19753 
19754 	// If we got this far, we never decided to
19755 	// block, so return false.
19756 	return 0;
19757 }
19758 
19759 // Caskey, Damon V.
19760 // 2018-09-17
19761 //
19762 // Runs all blocking conditions and returns true
19763 // if the attack should be blocked.
check_blocking_master(entity * ent,entity * other,s_collision_attack * attack)19764 int check_blocking_master(entity *ent, entity *other, s_collision_attack *attack)
19765 {
19766 	e_entity_type entity_type;
19767 
19768 	entity_type = ent->modeldata.type;
19769 
19770 	// Check AI or player blocking rules.
19771 	if (entity_type & TYPE_PLAYER)
19772 	{
19773 		// For players, all we need to know is if they
19774 		// are in a blocking state. If not we exit.
19775 		if (!ent->blocking)
19776 		{
19777 			return 0;
19778 		}
19779 
19780 		// Verify entity can block the attack at all.
19781 		if (!check_blocking_eligible(ent, other, attack))
19782 		{
19783 			return 0;
19784 		}
19785 	}
19786 	else
19787 	{
19788 		// AI must pass a series of conditions
19789 		// before it may block attacks.
19790 		if (!check_blocking_rules(ent))
19791 		{
19792 			return 0;
19793 		}
19794 
19795 		// Now that we know AI is allowed
19796 		// to block let's find out if it
19797 		// wants to.
19798 		if (!check_blocking_decision(ent))
19799 		{
19800 			return 0;
19801 		}
19802 
19803 		// Verify entity can block the attack at all.
19804 		if (!check_blocking_eligible(ent, other, attack))
19805 		{
19806 			return 0;
19807 		}
19808 	}
19809 
19810 	// Looks like we made it through
19811 	// all the verifications. Return true.
19812 	return 1;
19813 }
19814 
19815 // Caskey, Damon V.
19816 // 2018-09-18
19817 //
19818 // Apply primary block settings, animations,
19819 // actions, and scripts.
set_blocking_action(entity * ent,entity * other,s_collision_attack * attack)19820 void set_blocking_action(entity *ent, entity *other, s_collision_attack *attack)
19821 {
19822 	// Execute the attacker's didhit script with blocked flag.
19823 	execute_didhit_script(other, ent, attack, 1);
19824 
19825 	// Set up blocking action and flag.
19826 	ent->takeaction = common_block;
19827 	set_blocking(ent);
19828 
19829 	// Stop movement.
19830 	ent->velocity.x = ent->velocity.z = 0;
19831 
19832 	// If we have guardpoints, then reduce them here.
19833 	if (ent->modeldata.guardpoints.max > 0)
19834 	{
19835 		ent->modeldata.guardpoints.current -= attack->guardcost;
19836 	}
19837 
19838 	// Blocked hit is still a hit, so
19839 	// increment the attacker's hit counter.
19840 	++other->animation->animhits;
19841 
19842 	// Execute our block script.
19843 	execute_didblock_script(ent, other, attack);
19844 }
19845 
19846 // Caskey, Damon V.
19847 // 2018-09-18
19848 //
19849 // Verify entity has blockpain and that attack
19850 // should trigger it.
check_blocking_pain(entity * ent,s_collision_attack * attack)19851 int check_blocking_pain(entity *ent, s_collision_attack *attack)
19852 {
19853 	// If we don't have blockpain,
19854 	// nothing else to do!
19855 	if (!self->modeldata.blockpain)
19856 	{
19857 		return 0;
19858 	}
19859 
19860 	// If blockpain is greater than attack
19861 	// force, we don't apply it.
19862 	if (self->modeldata.blockpain > attack->attack_force)
19863 	{
19864 		return 0;
19865 	}
19866 
19867 	return 1;
19868 }
19869 
19870 // Caskey, Damon V.
19871 // 2018-09-21
19872 //
19873 // Place entity into appropriate blocking animation.
set_blocking_animation(entity * ent,s_collision_attack * attack)19874 void set_blocking_animation(entity *ent, s_collision_attack *attack)
19875 {
19876 	// If we have an appropriate blockpain, lets
19877 	// apply it here.
19878 	if (check_blocking_pain(ent, attack))
19879 	{
19880 		set_blockpain(self, attack->attack_type, 1);
19881 	}
19882 	else
19883 	{
19884 		ent_set_anim(ent, ANI_BLOCK, 0);
19885 	}
19886 }
19887 
19888 // Caskey, Damon V.
19889 // 2018-09-21
19890 //
19891 // Perform a block.
do_passive_block(entity * ent,entity * other,s_collision_attack * attack)19892 void do_passive_block(entity *ent, entity *other, s_collision_attack *attack)
19893 {
19894 	// Place entity in blocking animation.
19895 	set_blocking_animation(ent, attack);
19896 
19897 	// Spawn the blocking flash.
19898 	spawn_attack_flash(ent, attack, attack->blockflash, ent->modeldata.bflash);
19899 
19900 	// Run blocking actions and scripts.
19901 	set_blocking_action(ent, other, attack);
19902 }
19903 
19904 // Caskey, Damon V.
19905 // 2018-09-18
19906 //
19907 // Handle flash spawning for hits. Will spawn and prepare an appropriate
19908 // flash effect entity if conditions are met.
spawn_attack_flash(entity * ent,s_collision_attack * attack,int attack_flash,int model_flash)19909 entity *spawn_attack_flash(entity *ent, s_collision_attack *attack, int attack_flash, int model_flash)
19910 {
19911 	int to_spawn;
19912 	entity *flash;
19913 
19914 	// Flash disabled by attack?
19915 	// We're done. Do nothing and exit.
19916 	if (attack->no_flash)
19917 	{
19918 		return NULL;
19919 	}
19920 
19921 	// If the model doesn't allow incoming custom
19922 	// flash  effects, default to the model's global flash.
19923 	//
19924 	// Otherwise we need to see if the custom
19925 	// attack flash index is valid. If it is, then
19926 	// we will use it to spawn a flash effect.
19927 	if (!ent->modeldata.noatflash)
19928 	{
19929 		// Valid custom flash index?
19930 		if (attack_flash >= 0)
19931 		{
19932 			to_spawn = attack_flash;
19933 		}
19934 		else
19935 		{
19936 			to_spawn = model_flash;
19937 		}
19938 	}
19939 	else
19940 	{
19941 		to_spawn = model_flash;
19942 	}
19943 
19944 	// Spawn the flash at last hit position.
19945 	flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, DIRECTION_LEFT, NULL, to_spawn, NULL);
19946 
19947 	// One last check to make sure we
19948 	// were able to spawn to flash entity.
19949 	if (flash)
19950 	{
19951 		// Set up basic properties.
19952 		flash->spawntype = SPAWN_TYPE_FLASH;
19953 		flash->base = lasthit.position.y;
19954 		flash->autokill |= AUTOKILL_ANIMATION_COMPLETE;
19955 
19956 		// If flipping enabled, flip the flash based on which
19957 		// side of entity the hit came from.
19958 		if (flash->modeldata.toflip)
19959 		{
19960 			flash->direction = (lasthit.position.x > ent->position.x);
19961 		}
19962 
19963 		// Run flash's spawn script.
19964 		execute_onspawn_script(flash);
19965 
19966 		return flash;
19967 	}
19968 
19969 	return NULL;
19970 }
19971 
do_attack(entity * e)19972 void do_attack(entity *e)
19973 {
19974     int them;
19975     int i, t;
19976     int force = 0;
19977     e_blocktype blocktype;
19978     entity *temp            = NULL;
19979     entity *def             = NULL;
19980     entity *topowner        = NULL;
19981     entity *otherowner      = NULL;
19982     entity *target          = NULL;
19983     s_anim      *current_anim;
19984     s_collision_attack *attack = NULL;
19985     int didhit              = 0;
19986     int didblock            = 0;    // So a different sound effect can be played when an attack is blocked
19987     int current_attack_id;
19988     int current_follow_id   = 0;
19989     //int hit_detected        = 0;    // Has a hit been detected?
19990 
19991 
19992 #define followed (current_anim!=e->animation)
19993     static unsigned int new_attack_id = 1;
19994 
19995     // Can't get hit after this
19996     if(level_completed)
19997     {
19998         return;
19999     }
20000 
20001     topowner = e; // trace the top owner, for projectile combo checking :)
20002     while(topowner->owner)
20003     {
20004         topowner = topowner->owner;
20005     }
20006 
20007 	// If any blast active, use projectile hit property.
20008     if(e->projectile & BLAST_ATTACK)
20009     {
20010         them = e->modeldata.projectilehit;
20011     }
20012     else
20013     {
20014         them = e->modeldata.candamage;
20015     }
20016 
20017     // Every attack gets a unique ID to make sure no one
20018     // gets hit more than once by the same attack
20019     current_attack_id = e->attack_id_outgoing;
20020 
20021     if(!current_attack_id)
20022     {
20023         ++new_attack_id;
20024         if(new_attack_id == 0)
20025         {
20026             new_attack_id = 1;
20027         }
20028         e->attack_id_outgoing = current_attack_id = new_attack_id;
20029     }
20030 
20031 
20032     current_anim = e->animation;
20033 
20034     for(i = 0; i < ent_max && !followed; i++)
20035     {
20036         target = ent_list[i];
20037 
20038         if(!target->exists)
20039         {
20040             continue;
20041         }
20042 
20043         // Check collision. If a collision
20044         // is found, the impacting
20045         // collision pointers are also
20046         // populated into lasthit, which
20047         // we will use below.
20048         if(!checkhit(e, target))
20049         {
20050             continue;
20051         }
20052 
20053         attack = lasthit.attack;
20054         force = attack->attack_force;
20055 
20056         // Verify target is alive.
20057         if(target->dead)
20058         {
20059             continue;
20060         }
20061 
20062         // Verify target is invincible,
20063         // or attack type is an item.
20064         // This is to allow item collection
20065         // even while invincible.
20066         if(target->invincible & INVINCIBLE_INTANGIBLE)
20067         {
20068             if(attack->attack_type != ATK_ITEM)
20069             {
20070                 continue;
20071             }
20072         }
20073 
20074         // If attack is set to only hit
20075         // one entity at a time (attackone),
20076         // we verify last hit (lasthit) is
20077         // set. If last hit is set and
20078         // differs from current target,
20079         // then we are trying to hit
20080         // another entity and should exit.
20081         if(current_anim->attackone)
20082         {
20083             if(e->lasthit)
20084             {
20085                 if(target != e->lasthit)
20086                 {
20087                     continue;
20088                 }
20089             }
20090         }
20091 
20092         // Verify target has a match to
20093         // projectile hit or can damage.
20094         if(!(target->modeldata.type & them))
20095         {
20096             continue;
20097         }
20098 
20099         // Pain time must have expired.
20100         // This is to allow reasonable delay
20101         // between hits so engine will not
20102         // run hit on every update.
20103         if(target->next_hit_time >= _time)
20104         {
20105             continue;
20106         }
20107 
20108         // Target takedamage flag
20109         // must be set.
20110         if(!target->takedamage)
20111         {
20112             continue;
20113         }
20114 
20115         // Attack IDs must be different.
20116         if(!multihitcheat){
20117 
20118 			// Kratus (20-04-21) multihit disabled
20119 			if((target->attack_id_incoming == current_attack_id || target->attack_id_incoming2 == current_attack_id || target->attack_id_incoming3 == current_attack_id || target->attack_id_incoming4 == current_attack_id ) && !attack->ignore_attack_id)
20120 			{
20121 				continue;
20122 			}
20123 		}
20124 		else
20125 		{
20126 			// Kratus (20-04-21) multihit enabled
20127 			if(target->attack_id_incoming == current_attack_id && !attack->ignore_attack_id)
20128 			{
20129 				continue;
20130 			}
20131 		}
20132 
20133 		// Target laying down? Exit if
20134         // attack only hits standing targets.
20135 		// Otherwise exit if attack only hits
20136 		// grounded targets.
20137         if(target->takeaction == common_lie)
20138         {
20139             if(attack->otg == OTG_NONE)
20140             {
20141                 continue;
20142             }
20143         }
20144 		else
20145 		{
20146             if(attack->otg == OTG_GROUND_ONLY)
20147             {
20148                 continue;
20149             }
20150         }
20151 
20152         // If in the air, then check the juggle cost.
20153         if(inair(target))
20154         {
20155             if(attack->jugglecost > target->modeldata.jugglepoints.current)
20156             {
20157                 continue;
20158             }
20159         }
20160 
20161         temp = self;
20162         self = target;
20163 
20164         // Execute the doattack scripts so author can set take action
20165         // before the hit code below does.
20166         execute_ondoattack_script(self, e, attack, EXCHANGE_RECIPIANT, current_attack_id);
20167         execute_ondoattack_script(e, self, attack, EXCHANGE_CONFERRER, current_attack_id);
20168 
20169         // 2010-12-31
20170         // Damon V. Caskey
20171 
20172         // If lasthit.confirm is not true, it must have been turned off by the author; almost
20173         // certainly with the ondoattack event scripts above. Skip the engine's
20174         // default hit handling below. Useful for scripting parry systems, alternate blocking,
20175         // or other custom collision events.
20176         if(lasthit.confirm)
20177         {
20178             didhit = 1;
20179         }
20180         else
20181         {
20182             // By White Dragon
20183             // This line: self = temp; is the fix for
20184             // !lasthit.confirm bug. Without it when
20185             // active lasthitc 0 the damagetaker has
20186             // weird speedy effect.
20187             self = temp;
20188             continue;
20189         }
20190 
20191         otherowner = self; // trace top owner for opponent
20192         while(otherowner->owner)
20193         {
20194             otherowner = otherowner->owner;
20195         }
20196 
20197         //if #01, if they are fired by the same owner, or the owner itself
20198         if(topowner == otherowner)
20199         {
20200             didhit = 0;
20201         }
20202 
20203         //if #02 , ground missle checking, and bullets wont hit each other
20204         if( (e->owner && self->owner) ||
20205                 (e->modeldata.ground && inair(e))  )
20206         {
20207             didhit = 0;
20208         }//end of if #02
20209 
20210         //if #05,   blocking code section
20211         if(didhit)
20212         {
20213             if(attack->attack_type == ATK_ITEM)
20214             {
20215                 do_item_script(self, e);
20216 
20217                 didfind_item(e);
20218                 return;
20219             }
20220 
20221 			// Set bomb projectile to detonate status if it
20222 			// hits or takes a hit.
20223             if(self->toexplode & EXPLODE_PREPARED)
20224             {
20225                 self->toexplode |= EXPLODE_DETONATE;
20226             }
20227 
20228             if(e->toexplode & EXPLODE_PREPARED)
20229             {
20230                 e->toexplode |= EXPLODE_DETONATE;
20231             }
20232 
20233             if(inair(self))
20234             {
20235                 self->modeldata.jugglepoints.current = self->modeldata.jugglepoints.current - attack->jugglecost;    //reduce available juggle points.
20236             }
20237 
20238             didblock = check_blocking_master(self, e, attack);
20239 
20240             // Blocking the attack?
20241             if(didblock)
20242             {
20243                 // Perform the blocking actions.
20244                 do_passive_block(self, e, attack);
20245             }
20246             // Counter the attack?
20247             else if(self->animation->counterrange &&	// Has counter range?
20248                     (self->animpos >= self->animation->counterrange->frame.min && self->animpos <= self->animation->counterrange->frame.max) &&  // Current frame within counter range frames?
20249                     !self->frozen &&
20250                     (self->energy_state.health_current > force || (self->energy_state.health_current-force <= 0 && (self->animation->counterrange->condition == COUNTERACTION_CONDITION_ALWAYS_RAGE))) &&   // Rage or not?
20251                     // counterrange conditions
20252                     ( (self->animation->counterrange->condition == COUNTERACTION_CONDITION_ALWAYS) || (self->animation->counterrange->condition == COUNTERACTION_CONDITION_ALWAYS_RAGE) ||
20253                     (self->animation->counterrange->condition == COUNTERACTION_CONDITION_HOSTILE && e->modeldata.type & them) ||
20254                     (self->animation->counterrange->condition == COUNTERACTION_CONDITION_HOSTILE_FRONT_NOFREEZE && !attack->no_block && !(self->direction == e->direction) && !attack->freeze ) )
20255 
20256                     ) {
20257 
20258                     // Take damage from attack?
20259                     if(self->animation->counterrange->damaged == COUNTERACTION_DAMAGE_NORMAL)
20260                     {
20261                         if (self->energy_state.health_current-force <= 0)
20262                         {
20263                             // White Dragon: commented the alternative method
20264                             /*s_collision_attack atk;
20265 
20266                             atk = emptyattack;
20267                             atk.attack_force = force;
20268                             atk.attack_drop = attack->attack_drop;
20269                             atk.attack_type = attack->attack_type;
20270 
20271                             atk.dropv.y = (float)DEFAULT_ATK_DROPV_Y;
20272                             atk.dropv.x = (float)DEFAULT_ATK_DROPV_X;
20273                             atk.dropv.z = (float)DEFAULT_ATK_DROPV_Z;
20274 
20275                             if (e) self->takedamage(e, &atk, 0);
20276                             else self->takedamage(self, &atk, 0);
20277 
20278                             self = temp;
20279                             return;*/
20280 
20281                             self->energy_state.health_current = 1; // rage
20282                         }
20283                         else
20284                         {
20285                             self->energy_state.health_current -= force;
20286                         }
20287                     }
20288 
20289                     current_follow_id = animfollows[self->animation->followup.animation - 1];
20290                     if(validanim(self, current_follow_id))
20291                     {
20292                         if(!self->modeldata.animation[current_follow_id]->attackone)
20293                         {
20294                             self->modeldata.animation[current_follow_id]->attackone = self->animation->attackone;
20295                         }
20296                         ent_set_anim(self, current_follow_id, 0);
20297 
20298                         // Kratus (20-04-21) used by the multihit glitch memorization
20299                         self->attack_id_incoming4 = self->attack_id_incoming3;
20300                         self->attack_id_incoming3 = self->attack_id_incoming2;
20301                         self->attack_id_incoming2 = self->attack_id_incoming;
20302                         self->attack_id_incoming = current_attack_id;
20303                     }
20304 
20305                     // Flash spawn.
20306                     spawn_attack_flash(self, attack, attack->blockflash, self->modeldata.bflash);
20307             }
20308             else if(self->takedamage(e, attack, 0))
20309             {
20310                 // This is the block for normal hits. The
20311                 // hit was not blocked, countered, or
20312                 // otherwise nullified, and this entity
20313                 // has takedamage() function. Let's
20314                 // process the hit.
20315 
20316                 execute_didhit_script(e, self, attack, 0);
20317                 ++e->animation->animhits;
20318 
20319                 e->lasthit = self;
20320 
20321                 // Flash spawn.
20322                 spawn_attack_flash(self, attack, attack->hitflash, self->modeldata.flash);
20323 
20324 
20325                 topowner->combotime = _time + combodelay; // well, add to its owner's combo
20326 
20327                 if(e->pausetime < _time || (inair(e) && !equalairpause))        // if equalairpause is set, inair(e) is nolonger a condition for extra pausetime
20328                 {
20329                     // Adds pause to the current animation
20330                     e->toss_time += attack->pause_add;      // So jump height pauses in midair
20331                     e->nextmove += attack->pause_add;      // xdir, zdir
20332                     e->nextanim += attack->pause_add;       //Pause animation for a bit
20333                     e->nextthink += attack->pause_add;      // So anything that auto moves will pause
20334                     e->pausetime = _time + attack->pause_add ; //UT: temporary solution
20335                 }
20336 
20337                 self->toss_time += attack->pause_add;       // So jump height pauses in midair
20338                 self->nextmove += attack->pause_add;      // xdir, zdir
20339                 self->nextanim += attack->pause_add;        //Pause animation for a bit
20340                 self->nextthink += attack->pause_add;       // So anything that auto moves will pause
20341 
20342             }
20343             else
20344             {
20345                 // If we made it to this block the hit was
20346                 // not countered or blocked, but the entity
20347                 // does not have a takedamage() function. It
20348                 // therefore must be a type that is meant
20349                 // to ignore hits.
20350 
20351                 didhit = 0;
20352                 continue;
20353             }
20354             // end of if #053
20355 
20356             // 2007 3 24, hmm, def should be like this
20357             if(didblock && !def)
20358             {
20359                 def = self;
20360             }
20361 
20362 			// Follow animations.
20363             if((e->animation->followup.animation) && // follow up?
20364                     (!e->animation->counterrange) && // This isn't suppossed to be a counter, right?
20365                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE) || (self->modeldata.type & e->modeldata.hostile)) &&                                // Does type matter?
20366                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE_NOKILL_NOBLOCK) || ((self->energy_state.health_current > 0) && !didblock)) &&                             // check if health or not blocking matters
20367                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE_NOKILL_NOBLOCK_NOGRAB) || ((self->energy_state.health_current > 0) && !didblock && cangrab(e, self)) ) && // check if nograb matters
20368                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE_NOKILL_BLOCK) || ((self->energy_state.health_current > 0) && didblock))                                   // check if health or blocking matters
20369               )
20370             {
20371                 current_follow_id = animfollows[e->animation->followup.animation - 1];
20372                 if(validanim(e, current_follow_id))
20373                 {
20374                     if(!e->modeldata.animation[current_follow_id]->attackone)
20375                     {
20376                         e->modeldata.animation[current_follow_id]->attackone = e->animation->attackone;
20377                     }
20378                     ent_set_anim(e, current_follow_id, 1);          // Then go to it!
20379                 }
20380                 //followed = 1; // quit loop, animation is changed
20381             }
20382 
20383             // Kratus (20-04-21) used by the multihit glitch memorization
20384             self->attack_id_incoming4 = self->attack_id_incoming3;
20385             self->attack_id_incoming3 = self->attack_id_incoming2;
20386             self->attack_id_incoming2 = self->attack_id_incoming;
20387             self->attack_id_incoming = current_attack_id;
20388 
20389 			// If hit, stop blocking.
20390 			if(self == def)
20391             {
20392                 self->blocking = didblock;
20393             }
20394 
20395             //2011/11/24 UT: move the next_hit_time logic here,
20396             // because block needs this as well otherwise blockratio causes instant death
20397             self->next_hit_time = _time + (attack->next_hit_time ? attack->next_hit_time : (GAME_SPEED / 5));
20398             self->nextattack = 0; // reset this, make it easier to fight back
20399         }//end of if #05
20400         self = temp;
20401 
20402     } // end of for
20403 
20404 
20405     // if ###
20406     if(didhit)
20407     {
20408         if(current_anim->energycost)
20409         {
20410             // well, dont check player or not - UTunnels. TODO: take care of that healthcheat
20411             if(e == topowner && current_anim->energycost->cost > 0 && nocost && !healthcheat)
20412             {
20413                 e->tocost = 1;    // Set flag so life is subtracted when animation is finished
20414             }
20415             else if(e != topowner && current_anim->energycost->cost > 0 && nocost && !healthcheat && !e->tocost) // if it is not top, then must be a shot
20416             {
20417                 if(current_anim->energycost->mponly != COST_TYPE_MP_THEN_HP && topowner->energy_state.mp_current > 0)
20418                 {
20419                     topowner->energy_state.mp_current -= current_anim->energycost->cost;
20420                     if(topowner->energy_state.mp_current < 0)
20421                     {
20422                         topowner->energy_state.mp_current = 0;
20423                     }
20424                 }
20425                 else
20426                 {
20427                     topowner->energy_state.health_current -= current_anim->energycost->cost;
20428                     if(topowner->energy_state.health_current <= 0)
20429                     {
20430                         topowner->energy_state.health_current = 1;
20431                     }
20432                 }
20433 
20434                 e->tocost = 1;    // Little backwards, but set to 1 so cost doesn't get subtracted multiple times
20435             }
20436         }
20437 
20438         // New blocking checks
20439         //04/27/2008 Damon Caskey: Added checks for defense property specific blockratio and type. Could probably use some cleaning.
20440         if(didblock && level->nohurt == DAMAGE_FROM_ENEMY_ON)
20441         {
20442             if(blockratio || def->defense[attack->attack_type].blockratio) // Is damage reduced?
20443             {
20444                 if (def->defense[attack->attack_type].blockratio)                       //Typed blockratio?
20445                 {
20446                     force = (int)(force * def->defense[attack->attack_type].blockratio);
20447                 }
20448                 else                                                                              //No typed. Use static block ratio.
20449                 {
20450                     force = force / 4;
20451                 }
20452 
20453                 /*
20454                 Block type handling. For backward compatibility we will use BLOCK_TYPE_MP_FIRST regardless
20455                 of defense setting if author has enabled mpblock. Otherwise the defender's blocktype
20456                 for incoming attack type will be used. Once this is determined, we will apply the
20457                 appropriate blocktype accordingly.
20458                 */
20459                 blocktype = mpblock ? BLOCK_TYPE_MP_FIRST : def->defense[attack->attack_type].blocktype;
20460 
20461                 switch (blocktype)
20462                 {
20463                     case BLOCK_TYPE_HP:
20464                         //Do nothing. This is so modders can overidde energycost mponly 1 with health only.
20465                         break;
20466 
20467                     case BLOCK_TYPE_MP_ONLY:
20468 
20469                         def->energy_state.mp_current -= force;
20470                         force = 0;
20471 
20472                         if(def->energy_state.mp_current < 0)
20473                         {
20474                             def->energy_state.mp_current = 0;
20475                         }
20476 
20477                     case BLOCK_TYPE_MP_FIRST:
20478 
20479                         def->energy_state.mp_current -= force;
20480 
20481                         /* If there isn't enough MP to cover force, subtract remaining MP from force and set MP to 0 */
20482                         if(def->energy_state.mp_current < 0)
20483                         {
20484                             force = -def->energy_state.mp_current;
20485                             def->energy_state.mp_current = 0;
20486                         }
20487                         else
20488                         {
20489                             force = 0;
20490                         }
20491 
20492                     case BLOCK_TYPE_BOTH:
20493 
20494                         def->energy_state.mp_current -= force;
20495 
20496                         if(def->energy_state.mp_current < 0)
20497                         {
20498                             def->energy_state.mp_current = 0;
20499                         }
20500                 }
20501 
20502                 if(force < def->energy_state.health_current)                    // If an attack won't deal damage, this line won't do anything anyway.
20503                 {
20504                     def->energy_state.health_current -= force;
20505                 }
20506                 else if(nochipdeath)                       // No chip deaths?
20507                 {
20508                     def->energy_state.health_current = 1;
20509                 }
20510                 else
20511                 {
20512                     temp = self;
20513                     self = def;
20514                     self->takedamage(e, attack, 0);           // Must be a fatal attack, then!
20515                     self = temp;
20516                 }
20517             }
20518         }
20519 
20520         if(!didblock)
20521         {
20522             topowner->rush.time = _time + (GAME_SPEED * rush[1]);
20523             topowner->rush.count.current++;
20524             if(topowner->rush.count.current > topowner->rush.count.max && topowner->rush.count.current > 1)
20525             {
20526                 topowner->rush.count.max = topowner->rush.count.current;
20527             }
20528         }
20529 
20530         if(didblock)
20531         {
20532             if(attack->blocksound >= 0)
20533             {
20534                 sound_play_sample(attack->blocksound, 0, savedata.effectvol, savedata.effectvol, 100);    // New custom block sound effect
20535             }
20536             else if(SAMPLE_BLOCK >= 0)
20537             {
20538                 sound_play_sample(SAMPLE_BLOCK, 0, savedata.effectvol, savedata.effectvol, 100);    // Default block sound effect
20539             }
20540         }
20541         else if(e->projectile & BLAST_ATTACK && SAMPLE_INDIRECT >= 0)
20542         {
20543             sound_play_sample(SAMPLE_INDIRECT, 0, savedata.effectvol, savedata.effectvol, 100);
20544         }
20545         else if(attack->hitsound >= 0)
20546         {
20547             t = 100 - (noslowfx ? 0 : (force - 5));
20548             if(t > 100)
20549             {
20550                 t = 100;
20551             }
20552             else if(t < 60)
20553             {
20554                 t = 60;
20555             }
20556             sound_play_sample(attack->hitsound, 0, savedata.effectvol, savedata.effectvol, t);
20557         }
20558 
20559         if(e->autokill & AUTOKILL_ATTACK_HIT)
20560         {
20561             kill_entity(e);
20562         }
20563     }//end of if ###
20564 #undef followed
20565 }
20566 
20567 
20568 // it can be useful for next changes
20569 /*static int is_obstacle_around(entity* ent, float threshold)
20570 {
20571     int i, j;
20572     int heightvar;
20573 
20574     if(ent->animation->size.y)
20575     {
20576         heightvar = ent->animation->size.y;
20577     }
20578     else
20579     {
20580         heightvar = ent->modeldata.size.y;
20581     }
20582 
20583     for(i = -1; i <= 1; i++ )
20584     {
20585         for(j = -1; j <= 1; j++ )
20586         {
20587             int w;
20588             entity *hito = find_ent_here(ent, ent->position.x+(i*threshold), ent->position.z+(j*threshold), (TYPE_OBSTACLE | TYPE_TRAP), NULL);
20589             entity *hitp = check_platform_between(ent->position.x+(i*threshold), ent->position.z+(j*threshold), ent->position.y, ent->position.y + heightvar, ent);
20590             int     hitw = (int)( (w = checkwall_below(ent->position.x+(i*threshold), ent->position.z+(j*threshold), T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[w].height > ent->position.y );
20591 
20592             if ( hito || hitp || hitw ) return 1;
20593         }
20594     }
20595 
20596     return 0;
20597 }*/
20598 
20599 // Caskey, Damon V.
20600 // 2018-04-20
20601 //
20602 // Go to landing frame if available. Also spawns an effect ("dust") entity if set.
check_landframe(entity * ent)20603 bool check_landframe(entity *ent)
20604 {
20605     entity *effect;
20606 
20607     // Must have a landframe.
20608     if(!ent->animation->landframe)
20609     {
20610         return 0;
20611     }
20612 
20613     // Can't be bound with a landframe override.
20614     if(check_bind_override(ent, BIND_OVERRIDE_LANDFRAME))
20615     {
20616         return 0;
20617     }
20618 
20619     // Can't be passed over current animation's frame count.
20620     if(ent->animation->landframe->frame > ent->animation->numframes)
20621     {
20622         return 0;
20623     }
20624 
20625     // Can't be already at or passed land frame.
20626     if(ent->animpos >= ent->animation->landframe->frame)
20627     {
20628         return 0;
20629     }
20630 
20631     // If a land frame dust effect entity is set, let's spawn it here.
20632     if(ent->animation->landframe->ent >= 0)
20633     {
20634         effect = spawn(ent->position.x, ent->position.z, ent->position.y, ent->direction, NULL, ent->animation->landframe->ent, NULL);
20635 
20636         if(effect)
20637         {
20638             effect->spawntype = SPAWN_TYPE_DUST_LAND;
20639             effect->base = ent->position.y;
20640             effect->autokill |= AUTOKILL_ANIMATION_COMPLETE;
20641             execute_onspawn_script(effect);
20642         }
20643     }
20644 
20645     update_frame(ent, ent->animation->landframe->frame);
20646 
20647     return 1;
20648 }
20649 
check_edge(entity * ent)20650 int check_edge(entity *ent)
20651 {
20652     float x = ent->position.x;
20653     float z = ent->position.z;
20654     float y = ent->position.y;
20655     e_direction dir = ent->direction;
20656     float height = 0.0f;
20657     float t_alt = 1.0f, t_walkoff = 1.0f, t_edge_default = 8.0f;
20658     float t_edge_x = t_edge_default, t_edge_z = t_edge_default / 2;
20659 
20660     if (ent->modeldata.edgerange.x > t_edge_x) t_edge_x = ent->modeldata.edgerange.x;
20661     if (ent->modeldata.edgerange.z > t_edge_z) t_edge_z = ent->modeldata.edgerange.z;
20662 
20663 	if(ent->animation->size.y) height = ent->animation->size.y;
20664     else height = ent->modeldata.size.y;
20665 
20666     //test base
20667     float base_left  = checkbase(x - t_edge_x, z, y + t_walkoff, ent);
20668     float base_right = checkbase(x + t_edge_x, z, y + t_walkoff, ent);
20669     float base_up    = checkbase(x, z - t_edge_z, y + t_walkoff, ent);
20670     float base_down  = checkbase(x, z + t_edge_z, y + t_walkoff, ent);
20671 
20672     entity *plat_left  = check_platform_between(x - t_edge_x, z, y + t_walkoff, y + height, ent);
20673     entity *plat_right = check_platform_between(x + t_edge_x, z, y + t_walkoff, y + height, ent);
20674     entity *plat_up    = check_platform_between(x, z - t_edge_z, y + t_walkoff, y + height, ent);
20675     entity *plat_down  = check_platform_between(x, z + t_edge_z, y + t_walkoff, y + height, ent);
20676 
20677     if ( (base_left < y - t_alt) && plat_left == NULL ) return EDGE_LEFT;
20678     else if ( (base_right < y - t_alt) && plat_right == NULL ) return EDGE_RIGHT;
20679 
20680     // priority based on direction check
20681     if (dir == DIRECTION_LEFT)
20682     {
20683         if ( (base_left < y - t_alt) && plat_left == NULL ) return EDGE_LEFT;
20684         else if ( (base_right < y - t_alt) && plat_right == NULL ) return EDGE_RIGHT;
20685     }
20686     else
20687     {
20688         if ( (base_right < y - t_alt) && plat_right == NULL ) return EDGE_RIGHT;
20689         else if ( (base_left < y - t_alt) && plat_left == NULL ) return EDGE_LEFT;
20690     }
20691 
20692     if (
20693          ((base_up   < y - t_alt) && plat_up == NULL) ||
20694          ((base_down < y - t_alt) && plat_down == NULL)
20695     ) return EDGE_LEFT + EDGE_RIGHT;
20696 
20697     return EDGE_NONE;
20698 }
20699 
check_gravity(entity * e)20700 void check_gravity(entity *e)
20701 {
20702     int heightvar;
20703     entity *other = NULL, *dust, *tempself, *plat = NULL;
20704     float gravity;
20705     float fmin, fmax;
20706 
20707     if(e->update_mark & UPDATE_MARK_CHECK_GRAVITY)
20708     {
20709         return;
20710     }
20711 
20712     tempself = self;
20713     self = e;
20714 
20715     adjust_base(self, &plat);
20716 
20717     if (self->position.y <= self->base) self->edge = check_edge(self); // && self->idling & IDLING_ACTIVE
20718     else self->edge = EDGE_NONE;
20719 
20720     if(!is_frozen(self) )// Incase an entity is in the air, don't update animations
20721     {
20722         if(self->animation->size.y)
20723         {
20724             heightvar = self->animation->size.y;
20725         }
20726         else
20727         {
20728             heightvar = self->modeldata.size.y;
20729         }
20730 
20731         // White Dragon: turn-off the hitwall flag if you're not near a obstacle. this help to avoid a hit loop
20732         /*if( (self->position.y <= self->base || !inair(self)) && self->velocity.y <= 0)
20733         {
20734             if ( self->hitwall && !is_obstacle_around(self,1.0) ) self->hitwall = 0;
20735         }*/
20736 
20737         if((self->falling || self->velocity.y || self->position.y != self->base) && self->toss_time <= _time)
20738         {
20739             if(heightvar && self->modeldata.subject_to_platform > 0 && self->velocity.y > 0)
20740             {
20741                 other = check_platform_above_entity(self);
20742             }
20743             else
20744             {
20745                 other = NULL;
20746             }
20747 
20748             if( other && other->position.y <= self->position.y + heightvar && !other->modeldata.nohithead)
20749             {
20750                 if(self->hithead == NULL) // bang! Hit the ceiling.
20751                 {
20752                     self->velocity.y = 0;
20753                     self->hithead = other;
20754                     execute_onblocka_script(self, other);
20755                 }
20756             }
20757             else
20758             {
20759                 self->hithead = NULL;
20760             }
20761             // gravity, antigravity factors
20762             self->position.y += self->velocity.y * 100.0 / GAME_SPEED;
20763             if(self->animation->antigrav)
20764             {
20765                 gravity = 0;
20766             }
20767             else
20768             {
20769                 gravity = (level ? level->gravity : default_level_gravity) * (1.0 - self->modeldata.antigravity);
20770             }
20771             if(self->modeldata.subject_to_gravity > 0)
20772             {
20773                 self->velocity.y += gravity * 100.0 / GAME_SPEED;
20774             }
20775 
20776             fmin = (level ? level->maxfallspeed : default_level_maxfallspeed);
20777             fmax = (level ? level->maxtossspeed : default_level_maxtossspeed);
20778 
20779             if(self->velocity.y < fmin)
20780             {
20781                 self->velocity.y = fmin;
20782             }
20783             else if(self->velocity.y > fmax)
20784             {
20785                 self->velocity.y = fmax;
20786             }
20787 
20788             // Dropframe set?
20789             if(self->animation->dropframe)
20790             {
20791                 // If falling and frame has not
20792                 // passed dropframe, set frame to dropframe.
20793                 if(self->velocity.y <= 0)
20794                 {
20795                     if(self->animpos < self->animation->dropframe->frame)
20796                     {
20797                         update_frame(self, self->animation->dropframe->frame);
20798                     }
20799                 }
20800             }
20801 
20802             if (self->velocity.y)
20803             {
20804                 execute_onmovea_script(self);    //Move A event.
20805             }
20806 
20807             if( self->idling && validanim(self, ANI_WALKOFF) && diff(self->position.y, self->base) > T_WALKOFF )
20808             {
20809                 entity *cplat = check_platform_below(self->position.x,self->position.z-1.0,self->position.y,self);
20810 
20811                 // White Dragon: fix for too low velocityz
20812                 if ( !cplat || (cplat && diff(get_platform_base(cplat),self->position.y) > T_WALKOFF) )
20813                 {
20814                     self->idling = IDLING_NONE;
20815                     self->ducking = DUCK_NONE;
20816                     self->takeaction = common_walkoff;
20817                     ent_set_anim(self, ANI_WALKOFF, 0);
20818                     self->landed_on_platform = plat = NULL;
20819                 }
20820             }
20821 
20822             // UTunnels: tossv <= 0 means land, while >0 means still rising, so
20823             // you wont be stopped if you are passing the edge of a wall
20824             if( self->position.y <= self->base || !inair(self))
20825             {
20826                 // 0+ means still rising. We need to be falling.
20827                 if(self->velocity.y <=0)
20828                 {
20829                     // No bind target, or binding set to ignore fall lands.
20830                     if(!check_bind_override(self, BIND_OVERRIDE_FALL_LAND))
20831                     {
20832                         self->position.y = self->base;
20833                         self->falling = 0;
20834 
20835                         if ( self->hitwall ) self->hitwall = 0;
20836 
20837                         // cust dust entity
20838                         if(self->modeldata.dust.fall_land >= 0 && self->velocity.y < -1 && self->drop)
20839                         {
20840                             dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.fall_land, NULL);
20841                             if(dust)
20842                             {
20843                                 dust->spawntype = SPAWN_TYPE_DUST_FALL;
20844                                 dust->base = self->position.y;
20845                                 dust->autokill |= AUTOKILL_ANIMATION_COMPLETE;
20846                                 execute_onspawn_script(dust);
20847                             }
20848                         }
20849 
20850                         // bounce/quake
20851                         if(tobounce(self) && self->modeldata.bounce)
20852                         {
20853                             int i;
20854                             self->velocity.x /= self->animation->bounce;
20855                             self->velocity.z /= self->animation->bounce;
20856                             toss(self, (-self->velocity.y) / self->animation->bounce);
20857                             if(level && !(self->modeldata.noquake & NO_QUAKE))
20858                             {
20859                                 level->quake = 4;    // Don't shake if specified
20860                             }
20861                             if(SAMPLE_FALL >= 0)
20862                             {
20863                                 sound_play_sample(SAMPLE_FALL, 0, savedata.effectvol, savedata.effectvol, 100);
20864                             }
20865                             if(self->modeldata.type & TYPE_PLAYER)
20866                             {
20867                                 if (savedata.joyrumble[self->playerindex]) control_rumble(self->playerindex, 1, 100 * (int)self->velocity.y / 2);
20868                             }
20869                             for(i = 0; i < MAX_PLAYERS; i++)
20870                             {
20871                                 if (savedata.joyrumble[i]) control_rumble(i, 1, 75 * (int)self->velocity.y / 2);
20872                             }
20873                         }
20874                         else if((!self->animation->move[self->animpos]->base || self->animation->move[self->animpos]->base < 0) &&
20875                                 (!self->animation->move[self->animpos]->axis.y || self->animation->move[self->animpos]->axis.y <= 0))
20876                         {
20877                             self->velocity.x = 0;
20878                             self->velocity.z = 0;
20879                             self->velocity.y = 0;
20880                         }
20881                         else
20882                         {
20883                             self->velocity.y = 0;
20884                         }
20885 
20886                         if(plat && !self->landed_on_platform && self->position.y <= plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT])
20887                         {
20888                             self->landed_on_platform = plat;
20889                         }
20890 
20891                         // Set landing frame if we have one.
20892                         check_landframe(self);
20893 
20894                         // Taking damage on a landing?
20895                         checkdamageonlanding();
20896 
20897                         // in case landing, set hithead to NULL
20898                         self->hithead = NULL;
20899                     }
20900                 }
20901             }// end of if - land checking
20902         }// end of if  - in-air checking
20903 
20904 		if(self->toss_time <= _time)
20905         {
20906             self->toss_time = _time + 1;
20907         }
20908 
20909     }//end of if
20910 
20911     self->update_mark |= UPDATE_MARK_CHECK_GRAVITY;
20912 
20913     self = tempself;
20914 }
20915 
check_lost()20916 int check_lost()
20917 {
20918     s_collision_attack attack;
20919     int osk = self->modeldata.offscreenkill ? self->modeldata.offscreenkill : DEFAULT_OFFSCREEN_KILL;
20920 
20921     if((self->position.z != ITEM_HIDE_POSITION_Z && (advancex - self->position.x > osk || self->position.x - advancex - videomodes.hRes > osk ||
20922                               (level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN && (advancey - self->position.z + self->position.y > osk || self->position.z - self->position.y - advancey - videomodes.vRes > osk)) ||
20923                               ((level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) && (self->position.z - self->position.y < -osk || self->position.z - self->position.y > videomodes.vRes + osk))		) )
20924             || self->position.y < 2 * PIT_DEPTH) //self->position.z<ITEM_HIDE_POSITION_Z, so weapon item won't be killed
20925     {
20926         if(self->modeldata.type & TYPE_PLAYER)
20927         {
20928             player_die();
20929         }
20930         else
20931         {
20932             kill_entity(self);
20933         }
20934         return 1;
20935     }
20936 
20937     // fall into a pit
20938     if(self->position.y < PIT_DEPTH)
20939     {
20940         if(!self->takedamage)
20941         {
20942             kill_entity(self);
20943         }
20944         else
20945         {
20946             attack          = emptyattack;
20947             attack.dropv	= default_model_dropv;
20948             attack.attack_force = self->energy_state.health_current;
20949             attack.attack_type  = ATK_PIT;
20950             self->takedamage(self, &attack, 0);
20951         }
20952         return 1;
20953     }
20954     else if(self->lifespancountdown < 0) //Lifespan expired.
20955     {
20956         if(!self->takedamage)
20957         {
20958             kill_entity(self);
20959         }
20960         else
20961         {
20962             attack          = emptyattack;
20963 			attack.dropv	= default_model_dropv;
20964             attack.attack_force = self->energy_state.health_current;
20965             attack.attack_type  = ATK_LIFESPAN;
20966             self->takedamage(self, &attack, 0);
20967         }
20968         return 1;
20969     }//else
20970 
20971     // Doom count down
20972     if(!is_frozen(self) && self->lifespancountdown != LIFESPAN_DEFAULT)
20973     {
20974         self->lifespancountdown--;
20975     }
20976 
20977     return 0;
20978 }
20979 
20980 // grab walk check
check_link_move(float xdir,float zdir)20981 void check_link_move(float xdir, float zdir)
20982 {
20983     float x, z, gx, gz;
20984     int tryresult;
20985     entity *tempself = self;
20986 
20987     gx = self->grabbing->position.x;
20988     gz = self->grabbing->position.z;
20989     x = self->position.x;
20990     z = self->position.z;
20991     self = self->grabbing;
20992     tryresult = self->trymove(xdir, zdir);
20993     self = tempself;
20994 
20995     if(tryresult != 1) // changed
20996     {
20997         xdir = self->grabbing->position.x - gx;
20998         zdir = self->grabbing->position.z - gz;
20999     }
21000     tryresult = self->trymove(xdir, zdir);
21001     if(tryresult != 1)
21002     {
21003         self->grabbing->position.x = self->position.x - x + gx;
21004         self->grabbing->position.z = self->position.z - z + gz;
21005     }
21006 }
21007 
check_ai()21008 void check_ai()
21009 {
21010     if(self->nextthink <= _time && !endgame)
21011     {
21012         self->update_mark |= UPDATE_MARK_CHECK_AI; //mark it
21013         // take actions
21014         if(self->takeaction)
21015         {
21016             self->takeaction();
21017         }
21018 
21019         // A.I. think
21020         if(self->think)
21021         {
21022             if(self->nextthink <= _time)
21023             {
21024                 self->nextthink = _time + THINK_SPEED;
21025             }
21026             // use noaicontrol flag to turn of A.I. think
21027             if(!self->noaicontrol)
21028             {
21029                 self->think();
21030             }
21031         }
21032 
21033         // Execute think script
21034         execute_think_script(self);
21035 
21036         // Used so all entities can have a spawn animation, and then just changes to the idle animation when done
21037         // move here to so players wont get stuck
21038         if((self->animation == self->modeldata.animation[ANI_SPAWN] || self->animation == self->modeldata.animation[ANI_RESPAWN]) && !self->animating /*&& (!inair(self)||!self->modeldata.subject_to_gravity)*/)
21039         {
21040             set_idle(self);
21041         }
21042     }
21043 }
21044 
check_basemap(int x,int z)21045 float check_basemap(int x, int z)
21046 {
21047     float maxbase = 0, base = T_MIN_BASEMAP;
21048     int i;
21049 
21050     if(!level)
21051     {
21052         return 0;
21053     }
21054 
21055     for(i = 0; i < level->numbasemaps; i++)
21056     {
21057         if(x >= level->basemaps[i].position.x && x < level->basemaps[i].position.x + level->basemaps[i].size.x &&
21058                 z >= level->basemaps[i].position.z && z < level->basemaps[i].position.z + level->basemaps[i].size.z)
21059         {
21060             base = level->basemaps[i].map[x - level->basemaps[i].position.x + level->basemaps[i].size.x * (z - level->basemaps[i].position.z)];
21061             if(base > maxbase)
21062             {
21063                 maxbase = base;
21064             }
21065         }
21066     }
21067     return base == T_MIN_BASEMAP ? base : maxbase;
21068 }
21069 
check_basemap_index(int x,int z)21070 int check_basemap_index(int x, int z)
21071 {
21072     float maxbase = 0, base = T_MIN_BASEMAP;
21073     int i, index = -1;
21074 
21075     if(!level)
21076     {
21077         return -1;
21078     }
21079 
21080     for(i = 0; i < level->numbasemaps; i++)
21081     {
21082         if(x >= level->basemaps[i].position.x && x < level->basemaps[i].position.x + level->basemaps[i].size.x &&
21083                 z >= level->basemaps[i].position.z && z < level->basemaps[i].position.z + level->basemaps[i].size.z)
21084         {
21085             base = level->basemaps[i].map[x - level->basemaps[i].position.x + level->basemaps[i].size.x * (z - level->basemaps[i].position.z)];
21086             if(base > maxbase)
21087             {
21088                 maxbase = base;
21089                 index = i;
21090             }
21091         }
21092     }
21093     return base == T_MIN_BASEMAP ? -1 : index;
21094 }
21095 
adjust_base(entity * e,entity ** pla)21096 void adjust_base(entity *e, entity **pla)
21097 {
21098     int wall = -1, hole = -1;
21099     float seta = 0, maxbase = 0;
21100     entity *other = NULL, *plat, *tempself;
21101 
21102     tempself = self;
21103     self = e;
21104 
21105     if(self->velocity.y > 0)
21106     {
21107         self->landed_on_platform = NULL;
21108     }
21109 
21110     if(self->modeldata.subject_to_platform > 0)
21111     {
21112         other = check_platform_below_entity(self);
21113         if(!other && self->landed_on_platform)
21114         {
21115             *pla = self->landed_on_platform = NULL;
21116         }
21117     }
21118     else
21119     {
21120         *pla = other = self->landed_on_platform = NULL;
21121     }
21122 
21123     if(other && !(other->update_mark & UPDATE_MARK_CHECK_GRAVITY))
21124     {
21125         check_gravity(other);
21126     }
21127 
21128     //no longer underneath?
21129     if(self->landed_on_platform && !testplatform(self->landed_on_platform, self->position.x, self->position.z, NULL))
21130     {
21131         *pla = self->landed_on_platform = NULL;
21132     }
21133 
21134     if(other && !self->landed_on_platform && self->position.y <= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT])
21135     {
21136         self->landed_on_platform = other;
21137     }
21138 
21139     if( (plat = self->landed_on_platform) )
21140     {
21141         if(!(plat->update_mark & UPDATE_MARK_CHECK_GRAVITY))
21142         {
21143             check_gravity(plat);
21144         }
21145         self->position.y = self->base = plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT];
21146     }
21147 
21148     *pla = other;
21149 
21150     // adjust base
21151     if(self->modeldata.no_adjust_base <= 0)
21152     {
21153         seta = (float)((self->animation->move[self->animpos]->base) ? (self->animation->move[self->animpos]->base) : (-1));
21154 
21155         // Checks to see if entity is over a wall and or obstacle, and adjusts the base accordingly
21156         //wall = checkwall_below(self->position.x, self->position.z);
21157         //find a wall below us
21158         if(self->modeldata.subject_to_wall > 0)
21159         {
21160             wall = checkwall_below(self->position.x, self->position.z, T_MAX_CHECK_ALTITUDE);
21161         }
21162         else
21163         {
21164             wall = -1;
21165         }
21166 
21167         //printf("stb:%d\n",self->modeldata.subject_to_basemap);
21168         if(self->modeldata.subject_to_basemap > 0) maxbase = check_basemap(self->position.x, self->position.z);
21169 
21170         if(self->modeldata.subject_to_hole > 0 &&
21171            ( (self->modeldata.subject_to_basemap > 0 && maxbase == T_MIN_BASEMAP) || (self->modeldata.subject_to_basemap <= 0) )
21172            )
21173         {
21174             hole = (wall < 0 && !other) ? checkhole_in(self->position.x, self->position.z, self->position.y) : 0;
21175             if ( hole )
21176             {
21177                 int holeind = checkholeindex_in(self->position.x, self->position.z, self->position.y);
21178                 if (holeind >= 0) execute_inhole_script(self, &level->holes[holeind], holeind);
21179             }
21180 
21181             if(seta < 0 && hole)
21182             {
21183                 self->base = T_MIN_BASEMAP;
21184                 ent_unlink(self);
21185             }
21186             else if(!hole && self->base == T_MIN_BASEMAP)
21187             {
21188                 if(self->position.y >= 0)
21189                 {
21190                     self->base = 0;
21191                 }
21192                 else
21193                 {
21194                     self->velocity.x = self->velocity.z = 0; // hit the hole border
21195                 }
21196             }
21197         }
21198 
21199         if(self->base != T_MIN_BASEMAP || wall >= 0)
21200         {
21201             if(other != NULL && other != self )
21202             {
21203                 self->base = (seta + self->altbase >= 0 ) * (seta + self->altbase) + (other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]);
21204             }
21205             else if(wall >= 0)
21206             {
21207                 //self->modeldata.subject_to_wall &&//we move this up to avoid some checking time
21208                 self->base = (seta + self->altbase >= 0 ) * (seta + self->altbase) + level->walls[wall].height;
21209             }
21210             else if(seta >= 0)
21211             {
21212                 self->base = (seta + self->altbase >= 0 ) * (seta + self->altbase);
21213             }
21214             else if(!self->animation->move[self->animpos]->axis.y || self->animation->move[self->animpos]->axis.y == 0)
21215             {
21216                 // No obstacle/wall or seta, so just set to 0
21217                 self->base = 0;
21218             }
21219         }
21220 
21221         if(self->base != T_MIN_BASEMAP && maxbase > self->base)
21222         {
21223             self->base = maxbase;
21224             // White Dragon: fix bug for floating entity on basemaps using a threshold
21225             if (self->velocity.y <= 0 && self->position.y - self->base <= T_WALKOFF)
21226             {
21227                 self->position.y = self->base;
21228             }
21229             //debug_printf("y:%f maxbase:%f",self->position.y,maxbase);
21230         }
21231     }
21232 
21233     self = tempself;
21234 }
21235 
21236 // Caskey Damon V.
21237 // 2018-04-08
21238 //
21239 // If entity has a timed color set, check
21240 // to see if colorset has expired. If so,
21241 // revert to default colorset and clear
21242 // the timer.
colorset_timed_expire(entity * ent)21243 int colorset_timed_expire(entity *ent)
21244 {
21245     // No color set time? Nothing to do.
21246     if(!ent->maptime)
21247     {
21248         return 0;
21249     }
21250 
21251     // If elapsed time has surpassed color
21252     // set time, then color set time is expired.
21253     // Revert entity back to default color set
21254     // and reset the color set timer.
21255     if(_time >= ent->maptime)
21256     {
21257         ent_set_colourmap(ent, ent->map);
21258         ent->maptime = 0;
21259 
21260         return 1;
21261     }
21262 
21263     // Color set _time was not expired.
21264     return 0;
21265 }
21266 
21267 
update_animation()21268 void update_animation()
21269 {
21270     int f;
21271     float scrollv = 0;
21272 
21273     if(level)
21274     {
21275         if(self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT)
21276         {
21277             self->direction = DIRECTION_RIGHT;
21278         }
21279         else if(self->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_LEFT)
21280         {
21281             self->direction = DIRECTION_LEFT;
21282         }
21283         else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT))
21284         {
21285             self->direction = DIRECTION_RIGHT;
21286         }
21287         else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT))
21288         {
21289             self->direction = DIRECTION_LEFT;
21290         }
21291         if(self->modeldata.type & TYPE_PANEL)
21292         {
21293             scrollv += self->modeldata.speed;
21294         }
21295         if(self->modeldata.scroll)
21296         {
21297             scrollv += self->modeldata.scroll;
21298         }
21299         if(scrollv)
21300         {
21301             self->position.x += scrolldx * scrollv;
21302             if(level->scrolldir == SCROLL_UP)
21303             {
21304                 scrollv = -scrollv;
21305             }
21306             self->position.y -= scrolldy * scrollv;
21307             self->base = -99999; // temporary fix otherwise it won't go underground
21308         }
21309     }
21310 
21311     if(!(self->idling & IDLING_ACTIVE) || (self->animnum == ANI_SLEEP && !self->animating))
21312     {
21313         self->sleeptime = _time + self->modeldata.sleepwait;
21314     }
21315 
21316 	// Invincible time has run out, turn off.
21317     if(self->invincible != INVINCIBLE_NONE && _time >= self->invinctime)
21318     {
21319         self->invincible    = INVINCIBLE_NONE;
21320         self->blink         = 0;
21321         self->invinctime    = 0;
21322         self->arrowon       = 0;
21323     }
21324 
21325     if(self->freezetime && _time >= self->freezetime)
21326     {
21327         unfrozen(self);
21328     }
21329 
21330     // Check for forced color set expiring.
21331     colorset_timed_expire(self);
21332 
21333     if(self->sealtime && _time >= self->sealtime) //Remove seal, special moves are available again.
21334     {
21335         self->seal = 0;
21336     }
21337     // Reset their escapecount if they aren't being spammed anymore.
21338     if(self->modeldata.escapehits && !self->inpain)
21339     {
21340         self->escapecount = 0;
21341     }
21342 
21343     if(self->nextanim == _time ||
21344             ((self->modeldata.type & TYPE_TEXTBOX) && self->modeldata.subtype != SUBTYPE_NOSKIP &&
21345              (bothnewkeys & (FLAG_JUMP | FLAG_ATTACK | FLAG_ATTACK2 | FLAG_ATTACK3 | FLAG_ATTACK4 | FLAG_SPECIAL)))) // Textbox will autoupdate if a valid player presses an action button
21346     {
21347         // Now you can display text and cycle through with any jump/attack/special unless SUBTYPE_NOSKIP
21348 
21349         f = self->animpos + self->animating;
21350 
21351         //Specified loop break frame.
21352         if(self->animation->loop.mode && self->animation->loop.frame.max)
21353         {
21354             if (f == self->animation->loop.frame.max)
21355             {
21356                 if(f < 0)
21357                 {
21358                     f = self->animation->numframes - 1;
21359                 }
21360                 else
21361                 {
21362                     f = 0;
21363                 }
21364 
21365                 if (self->animation->loop.frame.min)
21366                 {
21367                     f = self->animation->loop.frame.min;
21368                 }
21369             }
21370             else if((unsigned)f >= (unsigned)self->animation->numframes)
21371             {
21372                 self->animating = ANIMATING_NONE;
21373 
21374                 if(self->autokill & AUTOKILL_ANIMATION_COMPLETE)
21375                 {
21376                     kill_entity(self);
21377                     return;
21378                 }
21379             }
21380         }
21381         else if((unsigned)f >= (unsigned)self->animation->numframes)
21382         {
21383             if(f < 0)
21384             {
21385                 f = self->animation->numframes - 1;
21386             }
21387             else
21388             {
21389                 f = 0;
21390             }
21391 
21392             if(!self->animation->loop.mode)
21393             {
21394                 self->animating = ANIMATING_NONE;
21395 
21396                 if(self->autokill & AUTOKILL_ANIMATION_COMPLETE)
21397                 {
21398                     kill_entity(self);
21399                     return;
21400                 }
21401             }
21402             else
21403             {
21404                 if (self->animation->loop.frame.min)
21405                 {
21406                     f = self->animation->loop.frame.min;
21407                 }
21408             }
21409         }
21410 
21411         if(self->animating)
21412         {
21413             //self->nextanim = _time + (self->animation->delay[f]);
21414             self->update_mark |= UPDATE_MARK_UPDATE_ANIMATION; // frame updated, mark it
21415             // just switch frame to f, if frozen, expand_time will deal with it well
21416             update_frame(self, f);
21417 			bothnewkeys = 0; //stop mutiple skips.
21418         }
21419     }
21420 
21421 }
21422 
check_attack()21423 void check_attack()
21424 {
21425     // a normal fall
21426     if(self->falling && self->projectile == BLAST_NONE)
21427     {
21428         self->attack_id_outgoing = 0;
21429         return;
21430     }
21431     // on ground
21432     if(self->drop && !self->falling)
21433     {
21434         self->attack_id_outgoing = 0;
21435         return;
21436     }
21437 
21438     // Can't hit an opponent if you are frozen
21439     if(!is_frozen(self) && self->animation->collision_attack &&
21440             self->animation->collision_attack[self->animpos])
21441     {
21442                 do_attack(self);
21443         return;
21444     }
21445     self->attack_id_outgoing = 0;
21446 }
21447 
21448 // Caskey, Damon V.
21449 // 2018-04-08
21450 //
21451 // If actively charging, add energy to
21452 // entity based on chargerate property
21453 // and elapsed time. Returns 1 if
21454 // energy was added, 0 otherwise.
do_energy_charge(entity * ent)21455 int do_energy_charge(entity *ent)
21456 {
21457 	#define ENERGY_CHARGE_RATE 0.25
21458 
21459 	// Must be charging.
21460 	if (!ent->charging)
21461 	{
21462 		return 0;
21463 	}
21464 
21465 	// Must be time for a charge tick.
21466 	if (_time < ent->mpchargetime)
21467 	{
21468 		return 0;
21469 	}
21470 
21471 	// Add charge rate to current mp.
21472 	ent->energy_state.mp_current += ent->modeldata.chargerate;
21473 
21474 	// Time for next charge tick.
21475 	ent->mpchargetime = _time + (GAME_SPEED * ENERGY_CHARGE_RATE);
21476 
21477 	return 1;
21478 
21479 	#undef ENERGY_CHARGE_RATE
21480 }
21481 
21482 
update_health()21483 void update_health()
21484 {
21485     //12/30/2008: Guardrate by OX. Guardpoints increase over time.
21486     if(self->modeldata.guardpoints.max > 0 && _time >= self->guardtime) // If this is > 0 then guardpoints are set..
21487     {
21488         if(self->blocking)
21489         {
21490             self->modeldata.guardpoints.current += (self->modeldata.guardrate / 2);
21491             if(self->modeldata.guardpoints.current > self->modeldata.guardpoints.max)
21492             {
21493                 self->modeldata.guardpoints.current = self->modeldata.guardpoints.max;
21494             }
21495         }
21496         else
21497         {
21498             self->modeldata.guardpoints.current += self->modeldata.guardrate;
21499             if(self->modeldata.guardpoints.current > self->modeldata.guardpoints.max)
21500             {
21501                 self->modeldata.guardpoints.current = self->modeldata.guardpoints.max;
21502             }
21503         }
21504         self->guardtime = _time + GAME_SPEED;    //Reset guardtime.
21505     }
21506 
21507     //Damage over time.
21508     damage_recursive(self);
21509 
21510     // this is for restoring mp by _time by tails
21511     // Cleaning and addition of mpstable by DC, 08172008.
21512     // stabletype 4 added by OX 12272008
21513     if(magic_type == 0 && !self->charging)
21514     {
21515         if(_time >= self->magictime)
21516         {
21517 
21518             // 1 Only recover MP > mpstableval.
21519             // 2 No recover. Drop MP if MP < mpstableval.
21520             // 3 Both: recover if MP if MP < mpstableval and drop if MP > mpstableval.
21521             // 4 Gain until stable, then fall to stable.
21522 			// 0 Default. Recover MP at all times.
21523 
21524 			if (self->modeldata.mpstable == 1)
21525             {
21526                 if (self->energy_state.mp_current < self->modeldata.mpstableval)
21527                 {
21528                     self->energy_state.mp_current += self->modeldata.mprate;
21529                 }
21530             }
21531             else if(self->modeldata.mpstable == 2)
21532             {
21533                 if (self->energy_state.mp_current > self->modeldata.mpstableval)
21534                 {
21535                     self->energy_state.mp_current -= self->modeldata.mpdroprate;
21536                 }
21537             }
21538             else if (self->modeldata.mpstable == 3)
21539             {
21540                 if (self->energy_state.mp_current < self->modeldata.mpstableval)
21541                 {
21542 
21543                     self->energy_state.mp_current += self->modeldata.mprate;
21544                 }
21545                 else if (self->energy_state.mp_current > self->modeldata.mpstableval)
21546                 {
21547                     self->energy_state.mp_current -= self->modeldata.mpdroprate;
21548                 }
21549             }
21550 
21551             // OX. Stabletype 4. Gain mp until it reaches max. Then it drops down to mpstableval.
21552             else if (self->modeldata.mpstable == 4)
21553             {
21554                 if(self->energy_state.mp_current <= self->modeldata.mpstableval)
21555                 {
21556                     self->modeldata.mpswitch = 0;
21557                 }
21558                 else if(self->energy_state.mp_current == self->modeldata.mp)
21559                 {
21560                     self->modeldata.mpswitch = 1;
21561                 }
21562 
21563                 if(self->modeldata.mpswitch == 1)
21564                 {
21565                     self->energy_state.mp_current -= self->modeldata.mpdroprate;
21566                 }
21567                 else if(self->modeldata.mpswitch == 0)
21568                 {
21569                     self->energy_state.mp_current += self->modeldata.mprate;
21570                 }
21571             }
21572             else
21573             {
21574                 self->energy_state.mp_current += self->modeldata.mprate;
21575             }
21576 
21577             self->magictime = _time + GAME_SPEED;    //Reset magictime.
21578         }
21579     }
21580 
21581     // Active MP charging?
21582     do_energy_charge(self);
21583 
21584     if(self->energy_state.mp_current > self->modeldata.mp)
21585     {
21586         self->energy_state.mp_current = self->modeldata.mp;    // Don't want to add more than the max
21587     }
21588 
21589     if(self->energy_state.health_old < self->energy_state.health_current)
21590     {
21591         self->energy_state.health_old++;
21592     }
21593     else if(self->energy_state.health_old > self->energy_state.health_current)
21594     {
21595         self->energy_state.health_old--;
21596     }
21597 
21598     if(self->energy_state.mp_old < self->energy_state.mp_current)
21599     {
21600         self->energy_state.mp_old++;
21601     }
21602     else if(self->energy_state.mp_old > self->energy_state.mp_current)
21603     {
21604         self->energy_state.mp_old--;
21605     }
21606 }
21607 
21608 // Caskey, Damon V.
21609 // 2019-01-18
21610 //
21611 // Free all members of a recursive damage list.
free_recursive_list(s_damage_recursive * head)21612 void free_recursive_list(s_damage_recursive * head)
21613 {
21614 	s_damage_recursive * cursor;
21615 
21616 	while (head != NULL)
21617 	{
21618 		cursor = head;
21619 		head = head->next;
21620 		free(cursor);
21621 	}
21622 }
21623 
21624 // Caskey, Damon V.
21625 // 2019-01-20
21626 //
21627 // Remove a single node from the recursive damage linked list.
free_damage_recursive_node(s_damage_recursive ** list,s_damage_recursive * node)21628 void free_damage_recursive_node(s_damage_recursive **list, s_damage_recursive *node)
21629 {
21630 	s_damage_recursive *cursor;
21631 	s_damage_recursive *previous;
21632 
21633 	// Initialize previous.
21634 	previous = NULL;
21635 
21636 	// Iterate each node of list. On each iteration, previous is
21637 	// set to cursor before cursor iterates.
21638 	for (cursor = *list; cursor != NULL; previous = cursor, cursor = cursor->next)
21639 	{
21640 		// Are we at target element?
21641 		if (cursor == node)
21642 		{
21643 			// If previous is NULL we're at the head.
21644 			if (previous == NULL)
21645 			{
21646 				// Move node from head's next to head.
21647 				*list = cursor->next;
21648 			}
21649 			else
21650 			{
21651 				// Move previous next to cursor next. This
21652 				// effectivly "skips" cursor in sequence.
21653 				previous->next = cursor->next;
21654 			}
21655 
21656 			// Deallocate the node.
21657 			free(cursor);
21658 
21659 			return;
21660 		}
21661 	}
21662 }
21663 
21664 // damage_recursive
21665 // Caskey, Damon V.
21666 // 2009-06-17
21667 // --2018-01-02 retooled from former common_dot.
21668 // --2019-01-16 Replace recursion array with linked list.
21669 //
21670 // Apply recursive damage (damage over time (dot)).
damage_recursive(entity * ent)21671 void damage_recursive(entity *ent)
21672 {
21673     int         force_final;    // Final force; total damage after defense and offense factors are applied.
21674     float       offense;        // Owner's offense.
21675     float       defense;        // target defense.
21676     s_collision_attack attack;  // Attack structure.
21677 	s_damage_recursive *cursor;
21678 
21679 	// Iterate target's recursive damage nodes.
21680 	for(cursor = ent->recursive_damage; cursor != NULL; cursor = cursor->next)
21681 	{
21682 		// If time has expired, destroy node and exit
21683 		// this loop iteration.
21684 		if (_time > cursor->time)
21685 		{
21686 			// If this is the head and there are no other
21687 			// recursive damage nodes, we need to delete
21688 			// the head AND set it to NULL. Otherwise, we
21689 			// only delete the node.
21690 			if (cursor == ent->recursive_damage && cursor->next == NULL)
21691 			{
21692 				free(cursor);
21693 				ent->recursive_damage = NULL;
21694 			}
21695 			else
21696 			{
21697 				free_damage_recursive_node(&ent->recursive_damage, cursor);
21698 			}
21699 
21700 			continue;
21701 		}
21702 
21703 		// If it is not yet time for a tick, exit
21704 		// this iteration of loop.
21705 		if (_time < cursor->tick)
21706 		{
21707 			continue;
21708 		}
21709 
21710 		// If target is not alive, exit this iteration of loop.
21711 		if (ent->energy_state.health_current <= 0)
21712 		{
21713 			continue;
21714 		}
21715 
21716 		// Reset next tick time.
21717 		cursor->tick = _time + (cursor->rate * GAME_SPEED / 100);
21718 
21719 		// Does this recursive damage affect HP?
21720 		if (cursor->mode & DAMAGE_RECURSIVE_MODE_HP)
21721 		{
21722 			// Recursive HP Damage Logic:
21723 			//
21724 			// Normally it is preferable to apply takedamage(),
21725 			// any time we want to damage a target, but because
21726 			// it breaks grabs and would spam the HUD,
21727 			// takedamage() is not tenable for every tick
21728 			// of a recursive damage effect. However, we DO want
21729 			// the owner to get credit, grabs to be broken, HUD
21730 			// to react, etc., if the target is KO'd.
21731 
21732 			// To handle both needs, we will first factor offense
21733 			// and defense manually to get a calculated force. If
21734 			// the calculated force is sufficient to KO target, and
21735 			// this recursive tick is allowed to KO, we will go ahead
21736 			// and apply takedamage() using the original recursive
21737 			// force (takedamage() automatically calculates offense
21738 			// and defense). This way the engine will treat KO tick as
21739 			// if it were a direct hit with all appropriate reactions
21740 			// and credit. Otherwise, we'll just subtract the calculated
21741 			// force directly from target's HP for a 'silent' damage effect.
21742 
21743 			// Populate remaining local vars we'll need
21744 			// to apply recursive HP damage.
21745 			force_final = cursor->force;
21746 
21747 			// Get owner's offense and target's defense
21748 			// factors for the recursive damage type.
21749 			offense = cursor->owner->offense_factors[cursor->type];
21750 			defense = ent->defense[cursor->type].factor;
21751 
21752 			// Calculate resulting force from any existing owner
21753 			// offense and target defense factors.
21754 			if (offense)
21755 			{
21756 				force_final = (int)(cursor->force * offense);
21757 			}
21758 
21759 			if (defense)
21760 			{
21761 				force_final = (int)(force_final * defense);
21762 			}
21763 
21764 			// Is calculated force enough to KO target?
21765 			// Is this recursive damage allowed to KO?
21766 			if (force_final >= ent->energy_state.health_current)
21767 			{
21768 				// Is this recursive damage allowed to KO?
21769 				if (!(cursor->mode & DAMAGE_RECURSIVE_MODE_NON_LETHAL))
21770 				{
21771 					// Does target have a takedamage structure? If so
21772 					// we can use takedamage() for the finishing damage.
21773 					// Otherwise it must be a none type or some other
21774 					// exceptional entity like a projectile. In that case
21775 					// we will simply kill it.
21776 					if (ent->takedamage)
21777 					{
21778 						// Populate attack structure with
21779 						// our recursive damage values.
21780 						attack = emptyattack;
21781 						attack.attack_type = cursor->type;
21782 						attack.attack_force = force_final;
21783 						attack.dropv = default_model_dropv;
21784 
21785 						// Apply takedamage(). The engine will
21786 						// take care of everything else damage
21787 						// related.
21788 						ent->takedamage(cursor->owner, &attack, 0);
21789 					}
21790 					else
21791 					{
21792 						// Kill target instantly.
21793 						kill_entity(ent);
21794 					}
21795 				}
21796 				else
21797 				{
21798 					// Recursive damage is not allowed to KO.
21799 					// Just set target's HP to minimum value.
21800 					ent->energy_state.health_current = 1;
21801 
21802 					// Execute the target's takedamage script.
21803 					execute_takedamage_script(ent, cursor->owner, &attack);
21804 				}
21805 			}
21806 			else
21807 			{
21808 				// Calculated damage is insufficient to KO.
21809 				// Subtract directly from target's HP.
21810 				ent->energy_state.health_current -= force_final;
21811 
21812 				// Execute the target's takedamage script.
21813 				execute_takedamage_script(ent, cursor->owner, &attack);
21814 			}
21815 		}
21816 
21817 		// Does this recursive damage affect MP?
21818 		if (cursor->mode & DAMAGE_RECURSIVE_MODE_MP)
21819 		{
21820 			// Recursive MP Damage Logic:
21821 
21822 			// Could not be more simple. Subtract
21823 			// recursive force from MP. If MP would
21824 			// end with negative value, set 0.
21825 
21826 			// Subtract force from MP.
21827 			ent->energy_state.mp_current -= cursor->force;
21828 
21829 			// Stabilize MP at 0.
21830 			if (ent->energy_state.mp_current < 0)
21831 			{
21832 				ent->energy_state.mp_current = 0;
21833 			}
21834 		}
21835 	}
21836 }
21837 
adjust_bind(entity * e)21838 void adjust_bind(entity *e)
21839 {
21840 	#define ADJUST_BIND_SET_ANIM_RESETABLE 1
21841 	#define ADJUST_BIND_NO_FRAME_MATCH -1
21842 
21843 	// Exit if there is no bind target.
21844 	if (!e->binding.ent)
21845 	{
21846 		return;
21847 	}
21848 
21849 	// Run bind update script on the bind target.
21850 	execute_on_bind_update_other_to_self(e->binding.ent, e, &e->binding);
21851 
21852 	// Run bind update script on *e (entity performing bind).
21853 	execute_on_bind_update_self_to_other(e, e->binding.ent, &e->binding);
21854 
21855 	if (e->binding.match)
21856 	{
21857 		int				frame;
21858 		e_animations	animation;
21859 
21860 		// If a defined value is requested,
21861 		// use the binding member value.
21862 		// Otherwise use target's current value.
21863 		if (e->binding.match & BIND_ANIMATION_DEFINED)
21864 		{
21865 			animation = e->binding.animation;
21866 		}
21867 		else
21868 		{
21869 			animation = e->binding.ent->animnum;
21870 		}
21871 
21872 		// Are we NOT currently playing the target animation?
21873 		if (e->animnum != animation)
21874 		{
21875 			// If we don't have the target animation
21876 			// and animation kill flag is set, then
21877 			// we kill ourselves and exit the function.
21878 			if (!validanim(e, animation))
21879 			{
21880 				// Don't have the animation? Kill ourself.
21881 				if (e->binding.match & BIND_ANIMATION_REMOVE)
21882 				{
21883 					kill_entity(e);
21884 				}
21885 
21886 				// Cancel the bind and exit.
21887 				e->binding.ent = NULL;
21888 				return;
21889 			}
21890 
21891 			// Made it this far, we must have the target
21892 			// animation, so let's apply it.
21893 			ent_set_anim(e, animation, ADJUST_BIND_SET_ANIM_RESETABLE);
21894 		}
21895 
21896 
21897 		// If a defined value is requested,
21898 		// use the binding member value.
21899 		// If target value is requested use
21900 		// target's current value (duh).
21901 		// if no frame match at all requested
21902 		// then set ADJUST_BIND_NO_FRAME_MATCH
21903 		// so frame matching logic is skipped.
21904 
21905 		if (e->binding.match & BIND_ANIMATION_FRAME_DEFINED)
21906 		{
21907 			frame = e->binding.frame;
21908 		}
21909 		else if (e->binding.match & BIND_ANIMATION_FRAME_TARGET)
21910 		{
21911 			frame = e->binding.ent->animpos;
21912 		}
21913 		else
21914 		{
21915 			frame = ADJUST_BIND_NO_FRAME_MATCH;
21916 		}
21917 
21918 		// Any frame match flag set?
21919 		if (frame != ADJUST_BIND_NO_FRAME_MATCH)
21920 		{
21921 			// Are we NOT currently playing the target frame?
21922 			if (e->animpos != frame)
21923 			{
21924 				// If we don't have the frame and frame kill flag is
21925 				// set, kill ourselves.
21926 				if ((e->animation->numframes -1) < frame)
21927 				{
21928 
21929 					if (e->binding.match & BIND_ANIMATION_FRAME_REMOVE)
21930 					{
21931 						kill_entity(e);
21932 
21933 						// Cancel the bind and exit.
21934 						e->binding.ent = NULL;
21935 						return;
21936 					}
21937 				}
21938 
21939 				// Made it this far, let's try to
21940 				// apply the frame.
21941 				update_frame(e, frame);
21942 			}
21943 		}
21944 	}
21945 
21946 	// Apply sort ID adjustment.
21947 	e->sortid = e->binding.ent->sortid + e->binding.sortid;
21948 
21949 	// Get and apply direction adjustment.
21950 	e->direction = direction_adjustment(e->direction, e->binding.ent->direction, e->binding.direction);
21951 
21952 	// Run bind positioning function to get an
21953 	// adjusted (or not) position result we apply
21954 	// to each axis. For X axis, we want to adjust
21955 	// relative to the bind target's direction, so
21956 	// we'll send the function an inverted offset if
21957 	// binding target is facing left.
21958 	e->position.z = binding_position(e->position.z, e->binding.ent->position.z, e->binding.offset.z, e->binding.positioning.z);
21959 	e->position.y = binding_position(e->position.y, e->binding.ent->position.y, e->binding.offset.y, e->binding.positioning.y);
21960 
21961 	if (e->binding.positioning.x == BIND_MODE_TARGET && e->binding.ent->direction == DIRECTION_LEFT)
21962 	{
21963 		e->position.x = binding_position(e->position.x, e->binding.ent->position.x, -e->binding.offset.x, e->binding.positioning.x);
21964 	}
21965 	else
21966 	{
21967 		e->position.x = binding_position(e->position.x, e->binding.ent->position.x, e->binding.offset.x, e->binding.positioning.x);
21968 	}
21969 
21970 	#undef ADJUST_BIND_SET_ANIM_RESETABLE
21971 	#undef ADJUST_BIND_NO_FRAME_MATCH
21972 }
21973 
21974 // Caskey, Damon V.
21975 // 2018-10-13
21976 //
21977 // Return an adjusted position for binding based
21978 // on positioning settings, offset, and current position.
binding_position(float position_default,float position_target,int offset,e_bind_mode positioning)21979 float binding_position(float position_default, float position_target, int offset, e_bind_mode positioning)
21980 {
21981 	switch (positioning)
21982 	{
21983 		case BIND_MODE_TARGET:
21984 
21985 			return position_target + offset;
21986 			break;
21987 
21988 		case BIND_MODE_LEVEL:
21989 
21990 			return offset;
21991 			break;
21992 
21993 		case BIND_MODE_NONE:
21994 		default:
21995 
21996 			// Leave position as-is.
21997 			return position_default;
21998 			break;
21999 	}
22000 }
22001 
22002 // Caskey, Damon V.
22003 // 2018-10-13
22004 //
22005 // Return an adjusted entity direction based
22006 // on orginal direction, target direction
22007 // and direction adjust setting.
direction_adjustment(e_direction direction_default,e_direction direction_target,e_direction_adjust adjustment)22008 e_direction direction_adjustment(e_direction direction_default, e_direction direction_target, e_direction_adjust adjustment)
22009 {
22010 	// Apply direction adjustment.
22011 	switch (adjustment)
22012 	{
22013 		default:
22014 		case DIRECTION_ADJUST_NONE:
22015 
22016 			// Use original direction.
22017 			return direction_default;
22018 			break;
22019 
22020 		case DIRECTION_ADJUST_SAME:
22021 
22022 			// Same as target direction.
22023 			return direction_target;
22024 			break;
22025 
22026 		case DIRECTION_ADJUST_OPPOSITE:
22027 
22028 			// Opposite of target direction.
22029 			if (direction_target == DIRECTION_LEFT)
22030 			{
22031 				return DIRECTION_RIGHT;
22032 			}
22033 			else
22034 			{
22035 				return DIRECTION_LEFT;
22036 			}
22037 
22038 			break;
22039 
22040 		case DIRECTION_ADJUST_RIGHT:
22041 
22042 			return DIRECTION_RIGHT;
22043 			break;
22044 
22045 		case DIRECTION_ADJUST_LEFT:
22046 
22047 			return DIRECTION_LEFT;
22048 			break;
22049 	}
22050 }
22051 
22052 // Caskey, Damon V.
22053 // 2018-09-08
22054 //
22055 // Return true if the target entity has a valid
22056 // bind target and match for the override argument.
check_bind_override(entity * ent,e_bind_override overriding)22057 int check_bind_override(entity *ent, e_bind_override overriding)
22058 {
22059     if(ent->binding.ent)
22060     {
22061         if(ent->binding.overriding & overriding)
22062         {
22063             return TRUE;
22064         }
22065     }
22066 
22067     return FALSE;
22068 }
22069 
check_move(entity * e)22070 void check_move(entity *e)
22071 {
22072     float x, z;
22073     entity *plat, *tempself;
22074 
22075     if((e->update_mark & UPDATE_MARK_CHECK_MOVE))
22076     {
22077         return;
22078     }
22079     tempself = self;
22080     self = e;
22081 
22082     x = self->position.x;
22083     z = self->position.z;
22084 
22085     // check moving platform
22086     if((plat = self->landed_on_platform) ) // on the platform?
22087     {
22088         //update platform first to get actual movex and movez
22089         if(!(plat->update_mark & UPDATE_MARK_CHECK_MOVE))
22090         {
22091             check_move(plat);
22092         }
22093         if(plat->movex || plat->movez)
22094         {
22095             if(self->trymove)
22096             {
22097                 if(self->grabbing)
22098                 {
22099                     check_link_move(plat->movex, plat->movez);
22100                 }
22101                 else if(!self->link)
22102                 {
22103                     self->trymove(plat->movex, plat->movez);
22104                 }
22105             }
22106             else
22107             {
22108                 self->position.x += plat->movex;
22109                 self->position.z += plat->movez;
22110             }
22111         }
22112     }
22113     if(!is_frozen(self) )
22114     {
22115         if(self->nextmove <= _time && (self->movex || self->movez) )
22116         {
22117             if(self->trymove)
22118             {
22119                 // only do linked move while grabwalking for now,
22120                 // otherwise some grab moves that use move/movez command may act strangely
22121                 if(self->grabbing && self->grabwalking)
22122                 {
22123                     check_link_move(self->movex, self->movez);
22124                 }
22125                 else// if(!self->link || self->grabbing)
22126                 {
22127                     if(1 != self->trymove(self->movex, self->movez) && self->idling)
22128                     {
22129                         self->pathblocked += _time % 2;
22130                     }
22131                     else
22132                     {
22133                         self->pathblocked = 0;
22134                     }
22135                 }
22136             }
22137             else
22138             {
22139                 self->position.x += self->movex;
22140                 self->position.z += self->movez;
22141             }
22142         }
22143         if(self->nextmove <= _time)
22144         {
22145             self->nextmove = _time + 1;
22146         }
22147 
22148     }
22149     self->movex = self->position.x - x;
22150     self->movez = self->position.z - z;
22151     self->update_mark |= UPDATE_MARK_CHECK_MOVE;
22152     self = tempself;
22153 }
22154 
ent_post_update(entity * e)22155 void ent_post_update(entity *e)
22156 {
22157     check_gravity(e);// check gravity
22158     check_entity_collision_for(e);
22159     check_move(e);
22160 
22161     adjust_bind(e);
22162 }
22163 
22164 // arrenge the list reduce its length
arrange_ents()22165 void arrange_ents()
22166 {
22167     int i, ind = -1;
22168     entity *temp;
22169     if(ent_count == 0)
22170     {
22171         return;
22172     }
22173     if(ent_max == ent_count)
22174     {
22175         for(i = 0; i < ent_max; i++)
22176         {
22177             if(ent_list[i]->exists)
22178             {
22179                 ent_post_update(ent_list[i]);
22180             }
22181         }
22182     }
22183     else
22184     {
22185         for(i = 0; i < ent_max; i++)
22186         {
22187             if(!ent_list[i]->exists && ind < 0)
22188             {
22189                 ind = i;
22190             }
22191             else if(ent_list[i]->exists && ind >= 0)
22192             {
22193                 temp = ent_list[i];
22194                 ent_list[i] = ent_list[ind];
22195                 ent_list[ind] = temp;
22196                 ind++;
22197             }
22198             if(ent_list[i]->exists)
22199             {
22200                 ent_post_update(ent_list[i]);
22201             }
22202         }
22203         ent_max = ent_count;
22204     }
22205     for(i = 0; i < ent_max; i++)
22206     {
22207         ent_list[i]->movex = ent_list[i]->movez = 0;
22208     }
22209 }
22210 
22211 // Update all entities that wish to think or animate in this cycle.
22212 // All loops are separated because "self" might die during a pass.
update_ents()22213 void update_ents()
22214 {
22215     int i;
22216     for(i = 0; i < ent_max; i++)
22217     {
22218         if(ent_list[i]->exists && _time != ent_list[i]->timestamp)// dont update fresh entity
22219         {
22220             self = ent_list[i];
22221             self->update_mark = UPDATE_MARK_NONE;
22222             if(level)
22223             {
22224                 check_lost();    // check lost caused by level scrolling or lifespan
22225             }
22226             if(!self->exists)
22227             {
22228                 continue;
22229             }
22230             // expand _time incase being frozen
22231             if(is_frozen(self))
22232             {
22233                 expand_time(self);
22234             }
22235             else
22236             {
22237 
22238                 execute_updateentity_script(self);// execute a script
22239                 if(!self->exists)
22240                 {
22241                     continue;
22242                 }
22243                 check_ai();// check ai
22244                 if(!self->exists)
22245                 {
22246                     continue;
22247                 }
22248                 update_animation(); // if not frozen, update animation
22249                 if(!self->exists)
22250                 {
22251                     continue;
22252                 }
22253                 check_attack();// Collission detection
22254                 if(!self->exists)
22255                 {
22256                     continue;
22257                 }
22258                 update_health();// Update displayed health
22259                 self->movex += self->velocity.x * self->speedmul * (100.0 / GAME_SPEED);
22260                 self->movez += self->velocity.z * self->speedmul * (100.0 / GAME_SPEED);
22261             }
22262         }
22263     }//end of for
22264     arrange_ents();
22265     /*
22266     if(time>=nextplan){
22267     	plan();
22268     	nextplan = time+GAME_SPEED/2;
22269     }*/
22270 }
22271 
22272 
display_ents()22273 void display_ents()
22274 {
22275     unsigned f;
22276     int i, z, wall = 0, wall2;
22277     entity *e = NULL;
22278     entity *other = NULL;
22279     int qx, qy, sy, sz, alty;
22280     int sortid;
22281     float basemap;
22282     float temp1, temp2;
22283     int useshadow = 0;
22284     int can_mirror = 0;
22285     int shadowz;
22286     s_drawmethod *drawmethod = NULL;
22287     s_drawmethod commonmethod;
22288     s_drawmethod shadowmethod;
22289     int use_mirror = (level && level->mirror);
22290 
22291     int o_scrx = screenx, o_scry = screeny, scrx, scry;
22292 
22293     if(level)
22294     {
22295         shadowz = SHADOW_Z;
22296     }
22297     else
22298     {
22299         shadowz = MIN_INT + 100;
22300     }
22301 
22302     for(i = 0; i < ent_max; i++)
22303     {
22304         if(ent_list[i] && ent_list[i]->exists)
22305         {
22306             e = ent_list[i];
22307             // in a loop need to initialize to reset prev val. to avoid bugs.
22308             z = basemap = qx = qy = sy = sz = alty = sortid = temp1 = temp2 = 0;
22309 
22310             if(Script_IsInitialized(e->scripts->ondraw_script))
22311             {
22312                 ent_stack[ent_stack_size++] = e;
22313             }
22314             //if(!e->exists) continue; // just in case kill is called in the script
22315 
22316             if(e->modeldata.hpbarstatus.size.x)
22317             {
22318                 drawenemystatus(e);
22319             }
22320 
22321 			sortid = e->sortid;
22322             scrx = o_scrx - ((e->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
22323             scry = o_scry - ((e->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
22324 
22325 			if(freezeall || !(e->blink && (_time % (GAME_SPEED / 10)) < (GAME_SPEED / 20)))
22326             {
22327                 float eheight = T_WALKOFF, eplatheight = 0;
22328 
22329                 // get the height of the entity
22330                 if ( e->animation->platform )
22331                 {
22332                     s_anim *anim = e->animation;
22333 
22334                     if ( anim->platform[e->animpos] )
22335                     {
22336                         if ( anim->platform[e->animpos][PLATFORM_HEIGHT] ) eplatheight += anim->platform[e->animpos][PLATFORM_HEIGHT];
22337                     }
22338                 }
22339                 if ( e->modeldata.size.y && eplatheight <= 0 ) eheight += e->modeldata.size.y;
22340                 else eheight += eplatheight;
22341 
22342                 // If special is being executed, display all entities regardless
22343                 f = e->animation->sprite[e->animpos];
22344 
22345                 //other = check_platform(e->position.x, e->position.z, e);
22346                 other = check_platform_below(e->position.x, e->position.z, e->position.y+eheight, e);
22347                 wall = checkwall_index(e->position.x, e->position.z);
22348 
22349                 if(e->modeldata.subject_to_basemap > 0) basemap = check_basemap(e->position.x, e->position.z);
22350 
22351                 if(f < sprites_loaded)
22352                 {
22353                     // var "z" takes into account whether it has a setlayer set, whether there are other entities on
22354                     // the same "z", in which case there is a layer offset, whether the entity is on an obstacle, and
22355                     // whether the entity is grabbing someone and has grabback set
22356 
22357                     z = (int)e->position.z;    // Set the layer offset
22358 
22359                     if(other && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !other->modeldata.setlayer)
22360                     {
22361                         float zdepth = (float)( (float)e->position.z - (float)other->position.z +
22362                                                 (float)other->animation->platform[other->animpos][PLATFORM_DEPTH] -
22363                                                 (float)other->animation->platform[other->animpos][PLATFORM_Z] );
22364 
22365                         if(
22366                             e->link														// Linked to entity.
22367 							&& ((e->modeldata.grabback && !e->grabbing)						// Have grab back AND not grabbing.
22368 								|| (e->link->modeldata.grabback && e->link->grabbing)		// Linked has grab back and is grabbing.
22369 								|| e->grabbing)												// Grabbing.
22370                         )
22371                         {
22372 
22373                             // Make sure entities get displayed in front of obstacle and grabbee
22374                             sortid = other->sortid + zdepth + 2;
22375                             e->sortid = sortid;
22376                             z = (int)( other->position.z + 2 );
22377                         }
22378 
22379                         else
22380                         {
22381                             //if ( e->model->type == TYPE_PLAYER ) debug_printf("zdepth: %f",zdepth);
22382 
22383                             // Entity should always display in front of the obstacle
22384                             sortid = other->sortid + zdepth + 1;
22385                             e->sortid = sortid;
22386                             z = (int)( other->position.z + 1 );
22387                         }
22388 
22389                     }
22390 
22391                     // In most cases we want any spawned entity to
22392                     // default in front of owner.
22393                     if(e->owner)
22394                     {
22395                         // If this entity is not an exception to the rule,
22396                         // move its display order in front of owner.
22397                         if (!(self->modeldata.aimove & AIMOVE1_STAR))
22398                         {
22399                             sortid = e->owner->sortid + 1;
22400                         }
22401                     }
22402 
22403                     if(e->modeldata.setlayer)
22404                     {
22405                         z = HOLE_Z + e->modeldata.setlayer;    // Setlayer takes precedence
22406                     }
22407 
22408                     drawmethod = e->animation->drawmethods ? getDrawMethod(e->animation, e->animpos) : NULL;
22409 
22410 					if(e->drawmethod->flag)
22411                     {
22412                         drawmethod = (e->drawmethod);
22413                     }
22414                     if(!drawmethod)
22415                     {
22416                         commonmethod = plainmethod;
22417                     }
22418                     else
22419                     {
22420                         commonmethod = *drawmethod;
22421                     }
22422                     drawmethod = &commonmethod;
22423 
22424                     if(e->modeldata.alpha >= 1 && e->modeldata.alpha <= MAX_BLENDINGS)
22425                     {
22426                         if(drawmethod->alpha == BLEND_MODE_MODEL)
22427                         {
22428                             drawmethod->alpha = e->modeldata.alpha;
22429                         }
22430                     }
22431 
22432 					// Color selection. If drawmethod does not yet have a color table pointer, then
22433 					// we need to find one here.
22434                     if(!drawmethod->table)
22435                     {
22436 						// Drawmethod remap by index. Is there a value?
22437                         if(drawmethod->remap >= 1)
22438                         {
22439 							// Does the value fall within range of tables loaded? If so, use
22440 							// value to locate the color table by index, and then populate
22441 							// drawmethod table value with the color table pointer.
22442 							if (drawmethod->remap <= e->modeldata.maps_loaded)
22443 							{
22444 								drawmethod->table = model_get_colourmap(&(e->modeldata), drawmethod->remap);
22445 							}
22446                         }
22447 
22448 						// Color selection by entity property. Does it have a value? Note that script functions
22449 						// and most text values in OpenBOR give the appearance this property is an integer index.
22450 						// In actuality those functions accept an index, but immediately use it to locate a color
22451 						// table pointer to populate this property. See ent_set_colourmap.
22452                         if(e->colourmap)
22453                         {
22454 							// We don't want to override drawmethods, so first check drawmethod
22455 							// remap to make sure it is disabled (0 = force default, 1+ force alternates).
22456 							// If it is (dsiabled) then use property value to populate drawmethod table.
22457                             if(drawmethod->remap < 0)
22458                             {
22459                                 drawmethod->table = e->colourmap;
22460                             }
22461                         }
22462 
22463 						// If we haven't populated the drawmethod table yet, use
22464 						// model's default color table.
22465                         if(!drawmethod->table)
22466                         {
22467                             drawmethod->table = e->modeldata.palette;
22468                         }
22469 
22470 						// If globalmap is true, then author wants entity to use the current
22471 						// global color table.
22472                         if(e->modeldata.globalmap)
22473                         {
22474 							// If we are in a level and the level has a palette index specified,
22475 							// then use that index to find the color table pointer and populate
22476 							// drawmethod table. Otherwise, just get the global color table pointer.
22477                             if(level && current_palette)
22478                             {
22479                                 drawmethod->table = level->palettes[current_palette - 1];
22480                             }
22481                             else
22482                             {
22483                                 drawmethod->table = pal;
22484                             }
22485                         }
22486                     }
22487 
22488 					// If we have a dying remap, let's check for the dying flash effect.
22489                     if(e->dying)
22490                     {
22491 						// This checks against both dying percentage thresholds and their associated
22492 						// timing. If any pass, then we can move on and apply a flash.
22493                         if((e->energy_state.health_current <= e->per1 && e->energy_state.health_current > e->per2 && (_time % (GAME_SPEED / 5)) < (GAME_SPEED / 10)) ||
22494                                 (e->energy_state.health_current <= e->per2 && (_time % (GAME_SPEED / 10)) < (GAME_SPEED / 20)))
22495                         {
22496 							// Have any HP left?
22497                             if(e->energy_state.health_current > 0 )
22498                             {
22499 								// Do we have a second dying map? If not, we just use map 1.
22500                                 if(e->dying2 > 0)
22501                                 {
22502 									// If health is between percentage 1 and 2, use map 1. Otherwise
22503 									// use map 2.
22504                                     if(e->energy_state.health_current <= e->per1 && e->energy_state.health_current > e->per2)
22505                                     {
22506                                         drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying);
22507                                     }
22508                                     else if(e->energy_state.health_current <= e->per2)
22509                                     {
22510                                         drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying2);
22511                                     }
22512                                 }
22513 								else
22514 								{
22515 									drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying);
22516 								}
22517                             }
22518                         }
22519                     }
22520 
22521 					// Draw the entity according to its facing.
22522                     if(e->direction == DIRECTION_LEFT)
22523                     {
22524 						// Reverse the drawmethod flipx.
22525                         drawmethod->flipx = !drawmethod->flipx;
22526 
22527 						// If the flip rotate is enabled, reverse the
22528 						// rotation setting.
22529 						if(drawmethod->fliprotate && drawmethod->rotate)
22530                         {
22531                             drawmethod->rotate = 360 - drawmethod->rotate;
22532                         }
22533                     }
22534 
22535 					// don't display if behind the mirror
22536                     if(!use_mirror || z > MIRROR_Z)
22537                     {
22538                         //just a simple check, doesn't work with mirror nor gfxshadow
22539                         if(drawmethod->clipw)
22540                         {
22541                             drawmethod->clipx += (int)(e->position.x - scrx);
22542                             drawmethod->clipy += (int)(e->position.z - e->position.y - scry);
22543                         }
22544                         spriteq_add_sprite((int)(e->position.x - scrx), (int)(e->position.z - e->position.y - scry), z, f, drawmethod, sortid);
22545                     }
22546 
22547                     can_mirror = (use_mirror && self->position.z > MIRROR_Z);
22548                     if(can_mirror)
22549                     {
22550                         spriteq_add_sprite((int)(e->position.x - scrx), (int)((2 * MIRROR_Z - e->position.z) - e->position.y - scry), 2 * PANEL_Z - z , f, drawmethod, ent_list_size * 100 - sortid);
22551                     }
22552                 }//end of if(f<sprites_loaded)
22553 
22554                 if(e->modeldata.gfxshadow == 1 && f < sprites_loaded) //gfx shadow
22555                 {
22556                     useshadow = (e->animation->shadow ? e->animation->shadow[e->animpos] : 1) && shadowcolor && light.y;
22557                     //printf("\n %d, %d, %d\n", shadowcolor, light.x, light.y);
22558 
22559                     if(useshadow && e->position.y >= 0 && (!e->modeldata.aironly || (e->modeldata.aironly && inair(e))))
22560                     {
22561                         wall = checkwall_below(e->position.x, e->position.z, e->position.y);
22562                         if(wall < 0)
22563                         {
22564                             alty = (int)e->position.y;
22565                             temp1 = -1 * e->position.y * light.x / 256; // xshift
22566                             temp2 = (float)(-alty * light.y / 256);               // zshift
22567                             qx = (int)(e->position.x - scrx/* + temp1*/);
22568                             qy = (int)(e->position.z - scry/* +  temp2*/);
22569                         }
22570                         else
22571                         {
22572                             alty = (int)(e->position.y - level->walls[wall].height);
22573                             temp1 = -1 * (e->position.y - level->walls[wall].height) * light.x / 256; // xshift
22574                             temp2 = (float)(-alty * light.y / 256);               // zshift
22575                             qx = (int)(e->position.x - scrx/* + temp1*/);
22576                             qy = (int)(e->position.z - scry /*+  temp2*/ - level->walls[wall].height);
22577                         }
22578 
22579                         wall2 = checkwall_below(e->position.x + temp1, e->position.z + temp2, e->position.y); // check if the shadow drop into a hole or fall on another wall
22580 
22581                         if(other && other != e && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !(e->modeldata.shadowbase&1) )
22582                         {
22583                             alty = (int)(e->position.y - (other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]));
22584                             temp1 = -1 * (e->position.y - (other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT])) * light.x / 256; // xshift
22585                             temp2 = (float)(-e->position.y * light.y / 256);
22586 
22587                             qx = (int)( e->position.x - scrx );
22588                             qy = (int)( e->position.z - scry - other->position.y - other->animation->platform[other->animpos][PLATFORM_HEIGHT] ); // + (other->animation->platform[other->animpos][PLATFORM_DEPTH]/2)
22589                             //qy = (int)( e->position.z - e->position.y - scry + (e->position.y-e->base) );
22590                         }
22591 
22592                         if(basemap > 0 && !(e->modeldata.shadowbase&1))
22593                         {
22594                             alty = (int)(e->position.y - basemap);
22595                             temp1 = -1 * (e->position.y - basemap) * light.x / 256; // xshift
22596                             temp2 = (float)(-alty * light.y / 256);               // zshift
22597                             qx = (int)(e->position.x - scrx);
22598                             qy = (int)(e->position.z - scry - basemap);
22599                         }
22600 
22601                         //TODO check platforms, don't want to go through the entity list again right now // && !other after wall2
22602                         if(!(checkhole(e->position.x + temp1, e->position.z + temp2) && wall2 < 0 && !other) ) //&& !(wall>=0 && level->walls[wall].height>e->position.y))
22603                         {
22604                             if(wall >= 0 && wall2 >= 0)
22605                             {
22606                                 alty += (int)(level->walls[wall].height - level->walls[wall2].height);
22607                                 /*qx += -1*(level->walls[wall].height-level->walls[wall2].height)*light.x/256;
22608                                 qy += (level->walls[wall].height-level->walls[wall2].height) - (level->walls[wall].height-level->walls[wall2].height)*light.y/256;*/
22609                             }
22610                             else if(wall >= 0)
22611                             {
22612                                 alty += (int)(level->walls[wall].height);
22613                                 /*qx += -1*level->walls[wall].height*light.x/256;
22614                                 qy += level->walls[wall].height - level->walls[wall].height*light.y/256;*/
22615                             }
22616                             else if(wall2 >= 0)
22617                             {
22618                                 alty -= (int)(level->walls[wall2].height);
22619                                 /*qx -= -1*level->walls[wall2].height*light.x/256;
22620                                 qy -= level->walls[wall2].height - level->walls[wall2].height*light.y/256;*/
22621                             }
22622 
22623                             /*if (other)
22624                             {
22625                                 alty += (int)(other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]);
22626                             }*/
22627 
22628                             // set 2D-LIKE shadow
22629                             if ( (e->modeldata.shadowbase&2) ) alty = temp1 = temp2 = 0;
22630 
22631                             sy = (2 * MIRROR_Z - qy) - 2 * scry;
22632 
22633                             if ( other && !(e->modeldata.shadowbase&1) ) z = other->position.z + 1;
22634                             else z = shadowz;
22635 
22636                             sz = PANEL_Z - HUD_Z;
22637 
22638                             if(e->animation->shadow_coords)
22639                             {
22640                                 if(e->direction == DIRECTION_RIGHT)
22641                                 {
22642                                     qx += e->animation->shadow_coords[e->animpos][0];
22643                                 }
22644                                 else
22645                                 {
22646                                     qx -= e->animation->shadow_coords[e->animpos][0];
22647                                 }
22648                                 qy += e->animation->shadow_coords[e->animpos][1];
22649                                 sy -= e->animation->shadow_coords[e->animpos][1];
22650                             }
22651                             shadowmethod = plainmethod;
22652                             shadowmethod.fillcolor = (shadowcolor > 0 ? shadowcolor : 0);
22653                             shadowmethod.alpha = shadowalpha;
22654                             shadowmethod.channelb = shadowmethod.channelg = shadowmethod.channelr = shadowopacity;
22655                             shadowmethod.table = drawmethod->table;
22656                             shadowmethod.scalex = drawmethod->scalex;
22657                             shadowmethod.flipx = drawmethod->flipx;
22658                             shadowmethod.scaley = light.y * drawmethod->scaley / 256;
22659                             shadowmethod.flipy = drawmethod->flipy;
22660                             shadowmethod.centery += alty;
22661                             if(shadowmethod.flipy)
22662                             {
22663                                 shadowmethod.centery = -shadowmethod.centery;
22664                             }
22665                             if(shadowmethod.scaley < 0)
22666                             {
22667                                 shadowmethod.scaley = -shadowmethod.scaley;
22668                                 shadowmethod.flipy = !shadowmethod.flipy;
22669                             }
22670                             shadowmethod.rotate = drawmethod->rotate;
22671                             shadowmethod.shiftx = drawmethod->shiftx + light.x;
22672 
22673                             spriteq_add_sprite(qx, qy, z, f, &shadowmethod, 0);
22674                             if(use_mirror)
22675                             {
22676                                 shadowmethod.flipy = !shadowmethod.flipy;
22677                                 shadowmethod.centery = -shadowmethod.centery;
22678                                 spriteq_add_sprite(qx, sy, sz, f, &shadowmethod, 0);
22679                             }
22680                         }
22681                     }//end of gfxshadow
22682                 }
22683                 else //plain shadow
22684                 {
22685                     useshadow = e->animation->shadow ? e->animation->shadow[e->animpos] : e->modeldata.shadow;
22686                     if(useshadow < 0)
22687                     {
22688                         useshadow = e->modeldata.shadow;
22689                     }
22690                     if(useshadow && e->position.y >= 0 && !(checkhole(e->position.x, e->position.z) && checkwall_below(e->position.x, e->position.z, e->position.y) < 0) && (!e->modeldata.aironly || (e->modeldata.aironly && inair(e))))
22691                     {
22692                         if(other && other != e && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !(e->modeldata.shadowbase&1))
22693                         {
22694                             qx = (int)(e->position.x - scrx);
22695                             qy =                 (int)(e->position.z  - other->position.y - other->animation->platform[other->animpos][PLATFORM_HEIGHT] - scry);
22696                             sy = (int)((2 * MIRROR_Z - e->position.z) - other->position.y - other->animation->platform[other->animpos][PLATFORM_HEIGHT] - scry);
22697 
22698                             z = (int)(other->position.z + 1);
22699                             sz = 2 * PANEL_Z - z;
22700                         }
22701                         else if(level && wall >= 0)// && e->position.y >= level->walls[wall].height)
22702                         {
22703                             qx = (int)(e->position.x - scrx);
22704                             qy = (int)(e->position.z - level->walls[wall].height - scry);
22705                             sy = (int)((2 * MIRROR_Z - e->position.z) - level->walls[wall].height - scry);
22706                             z = shadowz;
22707                             sz = PANEL_Z - HUD_Z;
22708                         }
22709                         else if(level && basemap > 0 && !(e->modeldata.shadowbase&1))
22710                         {
22711                             qx = (int)(e->position.x - scrx);
22712                             qy = (int)(e->position.z - basemap - scry);
22713                             sy = (int)((2 * MIRROR_Z - e->position.z) - basemap - scry);
22714                             z = shadowz;
22715                             sz = PANEL_Z - HUD_Z;
22716                         }
22717                         else
22718                         {
22719                             qx = (int)(e->position.x - scrx);
22720                             qy = (int)(e->position.z - scry);
22721                             sy = (int)((2 * MIRROR_Z - e->position.z) - scry);
22722                             z = shadowz;
22723                             sz = PANEL_Z - HUD_Z;
22724                         }
22725                         if(e->animation->shadow_coords)
22726                         {
22727                             if(e->direction == DIRECTION_RIGHT)
22728                             {
22729                                 qx += e->animation->shadow_coords[e->animpos][0];
22730                             }
22731                             else
22732                             {
22733                                 qx -= e->animation->shadow_coords[e->animpos][0];
22734                             }
22735                             qy += e->animation->shadow_coords[e->animpos][1];
22736                             sy -= e->animation->shadow_coords[e->animpos][1];
22737                         }
22738 
22739                         shadowmethod = plainmethod;
22740                         shadowmethod.alpha = BLEND_MULTIPLY + 1;
22741                         shadowmethod.flipx = !e->direction;
22742 
22743                         spriteq_add_sprite(qx, qy, z, shadowsprites[useshadow - 1], &shadowmethod, 0);
22744                         if(use_mirror)
22745                         {
22746                             spriteq_add_sprite(qx, sy, sz, shadowsprites[useshadow - 1], &shadowmethod, 0);
22747                         }
22748                     }//end of plan shadow
22749                 }
22750             }// end of blink checking
22751 
22752             if(e->arrowon)    // Display the players image while invincible to indicate player number
22753             {
22754                 if(e->modeldata.parrow[(int)e->playerindex][0] && e->invincible & INVINCIBLE_INTANGIBLE)
22755                 {
22756                     spriteq_add_sprite((int)(e->position.x - scrx + e->modeldata.parrow[(int)e->playerindex][1]), (int)(e->position.z - e->position.y - scry + e->modeldata.parrow[(int)e->playerindex][2]), (int)e->position.z, e->modeldata.parrow[(int)e->playerindex][0], NULL, sortid * 2);
22757                 }
22758             }
22759         }// end of if(ent_list[i]->exists)
22760 
22761         // reset vars for next loop
22762 
22763     }// end of for
22764 
22765     //defer ondraw script so it can manage spriteq better
22766     for(i = 0; i < ent_stack_size; i++)
22767     {
22768         execute_ondraw_script(ent_stack[i]);
22769     }
22770     ent_stack_size = 0;
22771 }
22772 
22773 
22774 
toss(entity * ent,float lift)22775 void toss(entity *ent, float lift)
22776 {
22777     if(!lift)
22778     {
22779         return;    //zero?
22780     }
22781     ent->toss_time = _time + 1;
22782     ent->velocity.y = lift;
22783     ent->position.y += 0.5;        // Get some altitude (needed for checks)
22784 }
22785 
22786 
22787 
findent(int types)22788 entity *findent(int types)
22789 {
22790     int i;
22791     for(i = 0; i < ent_max; i++)
22792     {
22793         // 2007-12-18, remove all nodieblink checking, because dead corpse with nodieblink 3 will be changed to TYPE_NONE
22794         // so if it is "dead" and TYPE_NONE, it must be a corpse
22795         if(ent_list[i]->exists && (ent_list[i]->modeldata.type & types) && !(ent_list[i]->dead && ent_list[i]->modeldata.type == TYPE_NONE))
22796         {
22797             return ent_list[i];
22798         }
22799     }
22800     return NULL;
22801 }
22802 
22803 
22804 
count_ents(int types)22805 int count_ents(int types)
22806 {
22807     int i;
22808     int count = 0;
22809     for(i = 0; i < ent_max; i++)
22810     {
22811         // 2007-12-18, remove all nodieblink checking, because dead corpse with nodieblink 3 will be changed to TYPE_NONE
22812         // so if it is "dead" and TYPE_NONE, it must be a corpse
22813         count += (ent_list[i]->exists && (ent_list[i]->modeldata.type & types) );
22814     }
22815     return count;
22816 }
22817 
isItem(entity * e)22818 int isItem(entity *e)
22819 {
22820     return e->modeldata.type & TYPE_ITEM;
22821 }
22822 
isSubtypeTouch(entity * e)22823 int isSubtypeTouch(entity *e)
22824 {
22825     return e->modeldata.subtype == SUBTYPE_TOUCH;
22826 }
22827 
isSubtypeWeapon(entity * e)22828 int isSubtypeWeapon(entity *e)
22829 {
22830     return e->modeldata.subtype == SUBTYPE_WEAPON;
22831 }
22832 
isSubtypeProjectile(entity * e)22833 int isSubtypeProjectile(entity *e)
22834 {
22835     return e->modeldata.subtype == SUBTYPE_PROJECTILE;
22836 }
22837 
canBeDamaged(entity * who,entity * bywhom)22838 int canBeDamaged(entity *who, entity *bywhom)
22839 {
22840     return (who->modeldata.candamage & bywhom->modeldata.type) == bywhom->modeldata.type;
22841 }
22842 
22843 //check if an item is usable by the entity
normal_test_item(entity * ent,entity * item)22844 int normal_test_item(entity *ent, entity *item)
22845 {
22846     return (
22847                isItem(item) &&
22848                (item->modeldata.stealth.hide <= ent->modeldata.stealth.detect) &&
22849                diff(item->position.x, ent->position.x) + diff(item->position.z, ent->position.z) < videomodes.hRes / 2 &&
22850                item->animation->vulnerable[item->animpos] && !item->blink &&
22851                (validanim(ent, ANI_GET) || (isSubtypeTouch(item) && canBeDamaged(item, ent))) &&
22852                (
22853                    (isSubtypeWeapon(item) && !ent->weapent && ent->modeldata.weapon &&
22854                     ent->modeldata.numweapons >= item->modeldata.weapnum && ent->modeldata.weapon[item->modeldata.weapnum - 1] >= 0)
22855                    || (isSubtypeProjectile(item) && !ent->weapent)
22856                    || (item->energy_state.health_current && (ent->energy_state.health_current < ent->modeldata.health) && ! isSubtypeProjectile(item) && ! isSubtypeWeapon(item))
22857                )
22858            );
22859 }
22860 
test_item(entity * ent,entity * item)22861 int test_item(entity *ent, entity *item)
22862 {
22863     if (!(
22864                 isItem(item) &&
22865                 item->animation->vulnerable[item->animpos] && !item->blink &&
22866                 (validanim(ent, ANI_GET) || (isSubtypeTouch(item) && canBeDamaged(item, ent)))
22867             ))
22868     {
22869         return 0;
22870     }
22871     if(isSubtypeProjectile(item) && ent->weapent)
22872     {
22873         return 0;
22874     }
22875     if(isSubtypeWeapon(item) &&
22876             (ent->weapent || !ent->modeldata.weapon ||
22877              ent->modeldata.numweapons < item->modeldata.weapnum ||
22878              ent->modeldata.weapon[item->modeldata.weapnum - 1] < 0)
22879       )
22880     {
22881         return 0;
22882     }
22883     return 1;
22884 }
22885 
player_test_pickable(entity * ent,entity * item)22886 int player_test_pickable(entity *ent, entity *item)
22887 {
22888     if(isSubtypeTouch(item))
22889     {
22890         return 0;
22891     }
22892     if(isSubtypeWeapon(item) && ent->modeldata.animal == 2)
22893     {
22894         return 0;
22895     }
22896     if(diff(ent->base , item->position.y) > 0.1)
22897     {
22898         return 0;
22899     }
22900     return test_item(ent, item);
22901 }
22902 
player_test_touch(entity * ent,entity * item)22903 int player_test_touch(entity *ent, entity *item)
22904 {
22905     if(!isSubtypeTouch(item))
22906     {
22907         return 0;
22908     }
22909     if(isSubtypeWeapon(item) && ent->modeldata.animal == 2)
22910     {
22911         return 0;
22912     }
22913     if(diff(ent->base , item->position.y) > 1)
22914     {
22915         return 0;
22916     }
22917     return test_item(ent, item);
22918 }
22919 
find_ent_here(entity * exclude,float x,float z,int types,int (* test)(entity *,entity *))22920 entity *find_ent_here(entity *exclude, float x, float z, int types, int (*test)(entity *, entity *))
22921 {
22922     int i;
22923     for(i = 0; i < ent_max; i++)
22924     {
22925         if( ent_list[i]->exists
22926                 && ent_list[i] != exclude
22927                 && (ent_list[i]->modeldata.type & types)
22928                 && diff(ent_list[i]->position.x, x) < (self->modeldata.grabdistance * 0.83333)
22929                 && diff(ent_list[i]->position.z, z) < (self->modeldata.grabdistance / 3)
22930                 && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
22931                 && (!test || test(exclude, ent_list[i]))
22932           )
22933         {
22934             return ent_list[i];
22935         }
22936     }
22937     return NULL;
22938 }
22939 
set_idle(entity * ent)22940 int set_idle(entity *ent)
22941 {
22942     ent->idling = IDLING_PREPARED;
22943     ent->attacking = ATTACKING_NONE;
22944     ent->inpain = 0;
22945     ent->rising = RISING_NONE;
22946     ent->ducking = DUCK_NONE;
22947     ent->inbackpain = 0;
22948     ent->falling = 0;
22949     ent->jumping = 0;
22950     ent->blocking = 0;
22951     common_idle_anim(ent);
22952     return 1;
22953 }
22954 
set_death(entity * iDie,int type,int reset)22955 int set_death(entity *iDie, int type, int reset)
22956 {
22957     int die = 0;
22958 
22959     //iDie->velocity.x = iDie->velocity.z = iDie->velocity.y = 0; // stop the target
22960     if(iDie->blocking && validanim(iDie, ANI_CHIPDEATH))
22961     {
22962         ent_set_anim(iDie, ANI_CHIPDEATH, reset);
22963         iDie->idling = IDLING_NONE;
22964         iDie->getting = 0;
22965         iDie->jumping = 0;
22966         iDie->charging = 0;
22967         iDie->attacking = ATTACKING_NONE;
22968         iDie->blocking = 0;
22969         iDie->inpain = 0;
22970         iDie->falling = 0;
22971         iDie->rising = RISING_NONE;
22972         iDie->ducking = DUCK_NONE;
22973         return 1;
22974     }
22975 
22976     if(type < 0 || type >= max_attack_types)
22977     {
22978         type = 0;
22979     }
22980 
22981     if ( iDie->inbackpain ) die = animbackdies[type];
22982     else die = animdies[type];
22983 
22984     if(validanim(iDie, die))
22985     {
22986         ent_set_anim(iDie, die, reset);
22987     }
22988     else if( iDie->inbackpain && validanim(iDie, animbackdies[0]) )
22989     {
22990         ent_set_anim(iDie, animbackdies[0], reset);
22991     }
22992     else if(validanim(iDie, animdies[type]))
22993     {
22994         if ( iDie->inbackpain ) reset_backpain(iDie);
22995         iDie->inbackpain = 0;
22996         ent_set_anim(iDie, animdies[type], reset);
22997     }
22998     else if(validanim(iDie, animdies[0]))
22999     {
23000         if ( iDie->inbackpain ) reset_backpain(iDie);
23001         iDie->inbackpain = 0;
23002         ent_set_anim(iDie, animdies[0], reset);
23003     }
23004     else
23005     {
23006         return 0;
23007     }
23008 
23009     iDie->idling = IDLING_NONE;
23010     iDie->getting = 0;
23011     iDie->jumping = 0;
23012     iDie->charging = 0;
23013     iDie->attacking = ATTACKING_NONE;
23014     iDie->blocking = 0;
23015     iDie->inpain = 0;
23016     iDie->falling = 0;
23017     iDie->rising = RISING_NONE;
23018     iDie->ducking = DUCK_NONE;
23019     if(iDie->frozen)
23020     {
23021         unfrozen(iDie);
23022     }
23023     return 1;
23024 }
23025 
23026 
set_fall(entity * ent,entity * other,s_collision_attack * attack,int reset)23027 int set_fall(entity *ent, entity *other, s_collision_attack *attack, int reset)
23028 {
23029     int fall = 0;
23030 
23031     if ( ent->inbackpain ) fall = animbackfalls[attack->attack_type];
23032     else fall = animfalls[attack->attack_type];
23033 
23034     if(validanim(ent, fall))
23035     {
23036         ent_set_anim(ent, fall, reset);
23037     }
23038     else if( ent->inbackpain && validanim(ent, animbackfalls[0]) )
23039     {
23040         ent_set_anim(ent, animbackfalls[0], reset);
23041     }
23042     else if( validanim(ent, animfalls[attack->attack_type]) )
23043     {
23044         if ( ent->inbackpain ) reset_backpain(ent);
23045         ent->inbackpain = 0;
23046         ent_set_anim(ent, animfalls[attack->attack_type], reset);
23047     }
23048     else if(validanim(ent, animfalls[0]))
23049     {
23050         if ( ent->inbackpain ) reset_backpain(ent);
23051         ent->inbackpain = 0;
23052         ent_set_anim(ent, animfalls[0], reset);
23053     }
23054     else
23055     {
23056         return 0;
23057     }
23058 
23059     ent->drop = 1;
23060     ent->inpain = 0;
23061     ent->rising = RISING_NONE;
23062     ent->idling = IDLING_NONE;
23063     ent->falling = 1;
23064     ent->jumping = 0;
23065     ent->ducking = DUCK_NONE;
23066     ent->getting = 0;
23067     ent->charging = 0;
23068     ent->attacking = ATTACKING_NONE;
23069     ent->blocking = 0;
23070     ent->nograb = 1;
23071 
23072     if(ent->frozen)
23073     {
23074         unfrozen(ent);
23075     }
23076     execute_onfall_script(ent, other, attack);
23077 
23078     return 1;
23079 }
23080 
set_rise(entity * iRise,int type,int reset)23081 int set_rise(entity *iRise, int type, int reset)
23082 {
23083     int rise = 0;
23084 
23085     if(type < 0 || type >= max_attack_types)
23086     {
23087         type = 0;
23088     }
23089 
23090     if ( iRise->inbackpain ) rise = animbackrises[type];
23091     else rise = animrises[type];
23092 
23093     if(validanim(iRise, rise))
23094     {
23095         ent_set_anim(iRise, rise, reset);
23096     }
23097     else if( iRise->inbackpain && validanim(iRise, animbackrises[0]) )
23098     {
23099         ent_set_anim(iRise, animbackrises[0], reset);
23100     }
23101     else if( validanim(iRise, animrises[type]) )
23102     {
23103         if ( iRise->inbackpain ) reset_backpain(iRise);
23104         iRise->inbackpain = 0;
23105         ent_set_anim(iRise, animrises[type], reset);
23106     }
23107     else if(validanim(iRise, animrises[0]))
23108     {
23109         if ( iRise->inbackpain ) reset_backpain(iRise);
23110         iRise->inbackpain = 0;
23111         ent_set_anim(iRise, animrises[0], reset);
23112     }
23113     else
23114     {
23115         return 0;
23116     }
23117 
23118     iRise->takeaction = common_rise;
23119     // Get up again
23120     iRise->drop = 0;
23121     iRise->falling = 0;
23122     iRise->rising |= RISING_RISE;
23123     iRise->rising &= ~RISING_ATTACK;
23124     iRise->projectile = BLAST_NONE;
23125     iRise->nograb = iRise->nograb_default; //iRise->nograb = 0;
23126     iRise->velocity.x = self->velocity.z = self->velocity.y = 0;
23127     iRise->modeldata.jugglepoints.current = iRise->modeldata.jugglepoints.max; //reset jugglepoints
23128     return 1;
23129 }
23130 
set_riseattack(entity * iRiseattack,int type,int reset)23131 int set_riseattack(entity *iRiseattack, int type, int reset)
23132 {
23133     int riseattack = 0;
23134 
23135     if( (!validanim(iRiseattack, animriseattacks[type]) ||
23136         (iRiseattack->inbackpain && !validanim(iRiseattack, animbackriseattacks[type]) && !validanim(iRiseattack, animriseattacks[type]))) &&
23137        iRiseattack->modeldata.riseattacktype == 1 )
23138     {
23139         type = 0;
23140     }
23141     if(iRiseattack->modeldata.riseattacktype == 0 || type < 0 || type >= max_attack_types)
23142     {
23143         type = 0;
23144     }
23145 
23146     if ( iRiseattack->inbackpain ) riseattack = animbackriseattacks[type];
23147     else riseattack = animriseattacks[type];
23148 
23149     if(validanim(iRiseattack, riseattack))
23150     {
23151         ent_set_anim(iRiseattack, riseattack, reset);
23152     }
23153     else if( iRiseattack->inbackpain && validanim(iRiseattack, animbackriseattacks[0]) )
23154     {
23155         ent_set_anim(iRiseattack, animbackriseattacks[0], reset);
23156     }
23157     else if( validanim(iRiseattack, animriseattacks[type]) )
23158     {
23159         if ( iRiseattack->inbackpain ) reset_backpain(iRiseattack);
23160         iRiseattack->inbackpain = 0;
23161         ent_set_anim(iRiseattack, animriseattacks[type], reset);
23162     }
23163     else if(validanim(iRiseattack, animriseattacks[0]))
23164     {
23165         if ( iRiseattack->inbackpain ) reset_backpain(iRiseattack);
23166         iRiseattack->inbackpain = 0;
23167         ent_set_anim(iRiseattack, animriseattacks[0], reset);
23168     }
23169     else
23170     {
23171         return 0;
23172     }
23173 
23174     iRiseattack->takeaction = common_attack_proc;
23175     self->staydown.riseattack_stall = 0;			//Reset riseattack delay.
23176     set_attacking(iRiseattack);
23177     iRiseattack->inpain = 0;
23178     iRiseattack->falling = 0;
23179     iRiseattack->ducking = DUCK_NONE;
23180     iRiseattack->rising &= ~RISING_RISE;
23181     iRiseattack->rising |= RISING_ATTACK;
23182     iRiseattack->drop = 0;
23183     iRiseattack->nograb = iRiseattack->nograb_default; //iRiseattack->nograb = 0;
23184     iRiseattack->modeldata.jugglepoints.current = iRiseattack->modeldata.jugglepoints.max; //reset jugglepoints
23185     return 1;
23186 }
23187 
set_blockpain(entity * ent,e_attack_types attack_type,int reset)23188 int set_blockpain(entity *ent, e_attack_types attack_type, int reset)
23189 {
23190     e_animations animation;
23191 
23192     // If attack type is out of bounds we
23193     // just use normal.
23194     if(attack_type < ATK_NORMAL || attack_type >= max_attack_types)
23195     {
23196         attack_type = ATK_NORMAL;
23197     }
23198 
23199     // In front or back?
23200     if (ent->inbackpain)
23201     {
23202         animation = animbackblkpains[attack_type];
23203     }
23204     else
23205     {
23206         animation = animblkpains[attack_type];
23207     }
23208 
23209     if(validanim(ent, animation))
23210     {
23211         ent_set_anim(ent, animation, reset);
23212     }
23213     else if( ent->inbackpain && validanim(ent, animbackblkpains[ATK_NORMAL]) )
23214     {
23215         ent_set_anim(ent, animbackblkpains[ATK_NORMAL], reset);
23216     }
23217     else if(validanim(ent, animblkpains[attack_type]))
23218     {
23219         if (ent->inbackpain)
23220         {
23221             reset_backpain(ent);
23222         }
23223 
23224         ent->inbackpain = 0;
23225         ent_set_anim(ent, animblkpains[attack_type], reset);
23226     }
23227     else if(validanim(ent, animblkpains[ATK_NORMAL]))
23228     {
23229         if (ent->inbackpain)
23230         {
23231             reset_backpain(ent);
23232         }
23233 
23234         ent->inbackpain = 0;
23235         ent_set_anim(ent, animblkpains[ATK_NORMAL], reset);
23236     }
23237     else
23238     {
23239         return 0;
23240     }
23241 
23242     ent->takeaction = common_block;
23243     set_blocking(self);
23244     ent->inpain = 1;
23245     ent->rising = RISING_NONE;
23246     ent->ducking = DUCK_NONE;
23247     ent_set_anim(ent, animblkpains[attack_type], reset);
23248     return 1;
23249 }
23250 
reset_backpain(entity * ent)23251 int reset_backpain(entity *ent)
23252 {
23253     if (ent->normaldamageflipdir >= 0)
23254     {
23255         if (ent->normaldamageflipdir == DIRECTION_RIGHT) ent->direction = DIRECTION_RIGHT;
23256         else ent->direction = DIRECTION_LEFT;
23257 
23258         if(ent->direction == DIRECTION_RIGHT) ent->velocity.x = -1*fabsf(ent->velocity.x);
23259         else ent->velocity.x = fabsf(ent->velocity.x);
23260 
23261         return 1;
23262     }
23263 
23264     return 0;
23265 }
23266 
check_backpain(entity * attacker,entity * defender)23267 int check_backpain(entity* attacker, entity* defender) {
23268     if ( !defender->modeldata.backpain ) return 0;
23269     if ( defender->inpain ) return 0;
23270     if ( defender->falling ) return 0;
23271     if ( defender->dead ) return 0;
23272     if ( ((!defender->direction && attacker->position.x > defender->position.x) || (defender->direction && attacker->position.x < defender->position.x)) )
23273     {
23274         defender->inbackpain = 1;
23275         return 1;
23276     } else if ( defender->inbackpain ) defender->inbackpain = 0;
23277 
23278     return 0;
23279 }
23280 
set_pain(entity * iPain,int type,int reset)23281 int set_pain(entity *iPain, int type, int reset)
23282 {
23283     int pain = 0;
23284 
23285     iPain->velocity.x = iPain->velocity.z = iPain->velocity.y = 0; // stop the target
23286     if(iPain->modeldata.guardpoints.max > 0 && iPain->modeldata.guardpoints.current <= 0)
23287     {
23288         pain = ANI_GUARDBREAK;
23289         iPain->modeldata.guardpoints.current = iPain->modeldata.guardpoints.max;
23290     }
23291     else if(type == -1 || type >= max_attack_types)
23292     {
23293         pain = ANI_GRABBED;
23294     }
23295     else
23296     {
23297         if ( iPain->inbackpain ) pain = animbackpains[type];
23298         else pain = animpains[type];
23299     }
23300 
23301 
23302     if(validanim(iPain, pain))
23303     {
23304         ent_set_anim(iPain, pain, reset);
23305     }
23306     else if( iPain->inbackpain && validanim(iPain, animbackpains[0]) )
23307     {
23308         ent_set_anim(iPain, animbackpains[0], reset);
23309     }
23310     else if( (type != -1 && type < max_attack_types) && validanim(iPain, animpains[type]) )
23311     {
23312         if ( iPain->inbackpain ) reset_backpain(iPain);
23313         iPain->inbackpain = 0;
23314         ent_set_anim(iPain, animpains[type], reset);
23315     }
23316     else if(validanim(iPain, animpains[0]))
23317     {
23318         if ( iPain->inbackpain ) reset_backpain(iPain);
23319         iPain->inbackpain = 0;
23320         ent_set_anim(iPain, animpains[0], reset);
23321     }
23322     else if(validanim(iPain, ANI_IDLE))
23323     {
23324         if ( iPain->inbackpain ) reset_backpain(iPain);
23325         iPain->inbackpain = 0;
23326         ent_set_anim(iPain, ANI_IDLE, reset);
23327     }
23328     else
23329     {
23330         return 0;
23331     }
23332 
23333 	iPain->idling = IDLING_NONE;
23334 	iPain->falling = 0;
23335 	iPain->rising = RISING_NONE;
23336 	iPain->ducking = DUCK_NONE;
23337 	iPain->projectile = BLAST_NONE;
23338 	iPain->drop = 0;
23339 	iPain->attacking = ATTACKING_NONE;
23340 	iPain->getting = 0;
23341 	iPain->charging = 0;
23342 	iPain->jumping = 0;
23343 	iPain->blocking = 0;
23344 	iPain->inpain = 1;
23345 	if(iPain->frozen) unfrozen(iPain);
23346 
23347     if(pain == ANI_GRABBED)
23348     {
23349         iPain->inpain = 0;
23350         iPain->rising = RISING_NONE;
23351         iPain->ducking = DUCK_NONE;
23352         if ( iPain->inbackpain ) reset_backpain(iPain);
23353         iPain->inbackpain = 0;
23354     }
23355 
23356     execute_onpain_script(iPain, type, reset);
23357     return 1;
23358 }
23359 
23360 /*
23361  * Copy animations from newmodel to model. If newmodel has anim that is not in model,
23362  * then copy that anim (but no frames with alloc_anim()) from newmodel to model
23363  */
normalize_anim_models(s_model * model,s_model * newmodel)23364 void normalize_anim_models(s_model *model, s_model *newmodel)
23365 {
23366 	int i = 0;
23367 
23368 	for(i = 0; i < max_animations; i++)
23369 	{
23370 		if(!model->animation[i] && newmodel->animation[i])
23371 		{
23372 			model->animation[i] = alloc_anim();
23373 		}
23374 	}
23375 
23376 	return;
23377 }
23378 
23379 //change model, anim_flag 1: reset animation 0: use original animation
set_model_ex(entity * ent,char * modelname,int index,s_model * newmodel,int anim_flag)23380 void set_model_ex(entity *ent, char *modelname, int index, s_model *newmodel, int anim_flag)
23381 {
23382     s_model *model = NULL;
23383     entity tempe;
23384     s_defense *dfs = NULL;
23385     float *ofs = NULL;
23386     int   i;
23387     int   type = ent->modeldata.type;
23388 
23389     model = ent->model;
23390     tempe.exists = 0;
23391 
23392     if(!newmodel)
23393     {
23394         if(index >= 0)
23395         {
23396             newmodel = model_cache[index].model;
23397         }
23398         else
23399         {
23400             newmodel = findmodel(modelname);
23401         }
23402     }
23403     if(!newmodel)
23404     {
23405         borShutdown(1, "Can't set model for entity '%s', model not found.\n", ent->name);
23406     }
23407     if(newmodel == model)
23408     {
23409         return;
23410     }
23411 
23412     if(!(newmodel->model_flag & MODEL_NO_COPY))
23413     {
23414         if(!newmodel->speed)
23415         {
23416             newmodel->speed = model->speed;
23417         }
23418         if(!newmodel->runspeed)
23419         {
23420             newmodel->runspeed = model->runspeed;
23421             newmodel->runjumpheight = model->runjumpheight;
23422             newmodel->runjumpdist = model->runjumpdist;
23423             newmodel->runupdown = model->runupdown;
23424             newmodel->runhold = model->runhold;
23425         }
23426         if(newmodel->icon.def           <   0)
23427         {
23428             newmodel->icon.def          = model->icon.def;
23429         }
23430         if(newmodel->icon.pain       <   0)
23431         {
23432             newmodel->icon.pain      = model->icon.pain;
23433         }
23434         if(newmodel->icon.get        <   0)
23435         {
23436             newmodel->icon.get       = model->icon.get;
23437         }
23438         if(newmodel->icon.die        <   0)
23439         {
23440             newmodel->icon.die       = model->icon.die;
23441         }
23442         if(newmodel->shadow         <   0)
23443         {
23444             newmodel->shadow        = model->shadow;
23445         }
23446         if(newmodel->knife          <   0)
23447         {
23448             newmodel->knife         = model->knife;
23449         }
23450         if(newmodel->pshotno        <   0)
23451         {
23452             newmodel->pshotno       = model->pshotno;
23453         }
23454         if(newmodel->bomb           <   0)
23455         {
23456             newmodel->bomb          = model->bomb;
23457         }
23458         if(newmodel->star           <   0)
23459         {
23460             newmodel->star          = model->star;
23461         }
23462         if(newmodel->flash          <   0)
23463         {
23464             newmodel->flash         = model->flash;
23465         }
23466         if(newmodel->bflash         <   0)
23467         {
23468             newmodel->bflash        = model->bflash;
23469         }
23470         if(newmodel->dust.fall_land        <   0)
23471         {
23472             newmodel->dust.fall_land       = model->dust.fall_land;
23473         }
23474         if(newmodel->dust.jump_land  <   0)
23475         {
23476             newmodel->dust.jump_land       = model->dust.jump_land;
23477         }
23478         if(newmodel->diesound       <   0)
23479         {
23480             newmodel->diesound      = model->diesound;
23481         }
23482 
23483         for(i = 0; i < max_animations; i++)
23484         {
23485             if(!newmodel->animation[i] && model->animation[i] && model->animation[i]->numframes > 0)
23486             {
23487                 newmodel->animation[i] = model->animation[i];
23488             }
23489         }
23490 		//normalize_anim_models(model, newmodel);
23491 
23492         // copy the weapon list if model flag is not set to use its own weapon list
23493         if(!(newmodel->model_flag & MODEL_NO_WEAPON_COPY))
23494         {
23495             newmodel->weapnum = model->weapnum;
23496             if(!newmodel->weapon)
23497             {
23498                 newmodel->weapon = model->weapon;
23499                 newmodel->numweapons = model->numweapons;
23500             }
23501         }
23502     }
23503 
23504     //Make a shallow copy of old entity values, not safe but easy.
23505     //Also copy offense and defense because they are more likely be used by weapons,
23506     //other references are left alone for now
23507     if(Script_IsInitialized(newmodel->scripts->onmodelcopy_script))
23508     {
23509         tempe = *ent;
23510         dfs = malloc(sizeof(*dfs) * max_attack_types);
23511         ofs = malloc(sizeof(*ofs) * max_attack_types);
23512         memcpy(dfs, ent->defense, sizeof(*dfs)*max_attack_types);
23513         memcpy(ofs, ent->offense_factors, sizeof(*ofs)*max_attack_types);
23514         tempe.defense = dfs;
23515         tempe.offense_factors = ofs;
23516     }
23517 
23518     ent_set_model(ent, newmodel->name, anim_flag);
23519 
23520     ent->modeldata.type = type;
23521 
23522     if((newmodel->model_flag & MODEL_NO_SCRIPT_COPY))
23523     {
23524         clear_all_scripts(ent->scripts, 0);
23525     }
23526 
23527     copy_all_scripts(newmodel->scripts, ent->scripts, 0);
23528     memcpy(ent->defense, ent->modeldata.defense, sizeof(*ent->defense)*max_attack_types);
23529     memcpy(ent->offense_factors, ent->modeldata.offense_factors, sizeof(*ent->offense_factors)*max_attack_types);
23530 
23531     ent_set_colourmap(ent, ent->map);
23532     if(Script_IsInitialized(ent->scripts->onmodelcopy_script))
23533     {
23534         execute_onmodelcopy_script(ent, &tempe);
23535         if(ofs)
23536         {
23537             free(ofs);
23538         }
23539         if(dfs)
23540         {
23541             free(dfs);
23542         }
23543     }
23544 }
23545 
set_weapon(entity * ent,int wpnum,int anim_flag)23546 void set_weapon(entity *ent, int wpnum, int anim_flag) // anim_flag added for scripted midair weapon changing
23547 {
23548     if(!ent)
23549     {
23550         return;
23551     }
23552 //printf("setweapon: %d \n", wpnum);
23553 
23554     if(ent->modeldata.type & TYPE_PLAYER) // save current weapon for player's weaploss 3
23555     {
23556         if(ent->modeldata.weaploss[0] == WEAPLOSS_TYPE_CHANGE)
23557         {
23558             player[(int)ent->playerindex].weapnum = wpnum;
23559         }
23560         else
23561         {
23562             player[(int)ent->playerindex].weapnum = level->setweap;
23563         }
23564     }
23565 
23566     if(ent->modeldata.weapon && wpnum > 0 && wpnum <= ent->modeldata.numweapons && ent->modeldata.weapon[wpnum - 1])
23567     {
23568         set_model_ex(ent, NULL, ent->modeldata.weapon[wpnum - 1], NULL, !anim_flag);
23569     }
23570     else
23571     {
23572         set_model_ex(ent, NULL, -1, ent->defaultmodel, 1);
23573     }
23574 }
23575 
23576 //////////////////////////////////////////////////////////////////////////
23577 //                  common A.I. code for enemies & NPCs
23578 //////////////////////////////////////////////////////////////////////////
23579 
23580 
melee_find_target()23581 entity *melee_find_target()
23582 {
23583     return NULL;
23584 }
23585 
long_find_target()23586 entity *long_find_target()
23587 {
23588     return NULL;
23589 }
23590 
block_find_target(int anim,int detect_adj)23591 entity *block_find_target(int anim, int detect_adj)
23592 {
23593     int i , min, max, instance, detect;
23594     int index = -1;
23595     min = 0;
23596     max = 9999;
23597     float diffx, diffz, diffd, diffo = 0;
23598     entity      *attacker;
23599 
23600     detect = detect_adj + self->modeldata.stealth.detect;
23601 
23602     //find the 'nearest' attacking one
23603     for(i = 0; i < ent_max; i++)
23604     {
23605         attacker = ent_list[i];
23606 
23607         for(instance = 0; instance < max_collisons; instance++)
23608         {
23609             if( attacker && attacker->exists && attacker != self //cant target self
23610                 && (attacker->modeldata.candamage & self->modeldata.type)
23611                 && (anim < 0 || (anim >= 0 && check_range_target_all(self, attacker, anim)))
23612                 && !attacker->dead //must be alive
23613                 && attacker->attacking != ATTACKING_NONE
23614                 && attacker->animation->collision_attack && attacker->animation->collision_attack[attacker->animpos] && attacker->animation->collision_attack[attacker->animpos]->instance
23615                 && ( !attacker->animation->collision_attack[attacker->animpos]->instance[instance] || (attacker->animation->collision_attack[attacker->animpos]->instance[instance] && attacker->animation->collision_attack[attacker->animpos]->instance[instance]->no_block == 0) )
23616                 && (diffd = (diffx = diff(attacker->position.x, self->position.x)) + (diffz = diff(attacker->position.z, self->position.z))) >= min
23617                 && diffd <= max
23618                 && (attacker->modeldata.stealth.hide <= detect) //Stealth factor less then perception factor (allows invisibility).
23619               )
23620             {
23621                 if(index < 0 || diffd < diffo)
23622                 {
23623                     index = i;
23624                     diffo = diffd;
23625 
23626                     continue;
23627                 }
23628             }
23629         }
23630     }
23631     if( index >= 0)
23632     {
23633         return ent_list[index];
23634     }
23635     return NULL;
23636 }
23637 
normal_find_target(int anim,int detect_adj)23638 entity *normal_find_target(int anim, int detect_adj)
23639 {
23640 
23641     /*
23642     normal_find_target
23643     Author unknown
23644     Date unknown
23645     ~Damon Caskey, 2011_07_22: Add support for detect adjustment.
23646 
23647     int anim:           Animation find range will be calculated by. Default to current animation if not passed.
23648     int detect_adj:     Local detection adjustment. Allows lesser or greater penetration of target's stealth for location.
23649     */
23650 
23651     int i;
23652     int min;
23653     int max;
23654     int detect;
23655     int index = -1;
23656     float diffx = 0;
23657     float diffz = 0;
23658     float diffd = 0;
23659     float diffo = 0;
23660 
23661     min = 0;
23662     max = 9999;
23663 
23664     detect = detect_adj + self->modeldata.stealth.detect;
23665 
23666     //find the 'nearest' one
23667     for(i = 0; i < ent_max; i++)
23668     {
23669         // Must exist.
23670         if(!ent_list[i]->exists)
23671         {
23672             continue;
23673         }
23674 
23675         // Can't be self.
23676         if(ent_list[i] == self)
23677         {
23678             continue;
23679         }
23680 
23681         // Must be hostile.
23682         if(!(ent_list[i]->modeldata.type & self->modeldata.hostile))
23683         {
23684             continue;
23685         }
23686 
23687         // If anim is defined, then then target must be
23688         // in range of animation.
23689         if(anim >= 0)
23690         {
23691             if(!check_range_target_all(self, ent_list[i], anim))
23692             {
23693                 continue;
23694             }
23695         }
23696 
23697         // Can't be dead.
23698         if(ent_list[i]->dead)
23699         {
23700             continue;
23701         }
23702 
23703         // Get X and Z differences between us and target. We then
23704         // add them up to get a total distance.
23705         diffx = diff(ent_list[i]->position.x, self->position.x);
23706         diffz = diff(ent_list[i]->position.z, self->position.z);
23707         diffd = diffx + diffz;
23708 
23709         // Distance must be within min and max.
23710         if(diffd <= min || diffd >= max)
23711         {
23712             continue;
23713         }
23714 
23715         // Stealth must not be greater than perception.
23716         if(ent_list[i]->modeldata.stealth.hide > detect)
23717         {
23718             continue;
23719         }
23720 
23721 
23722         if(index < 0
23723            || (index >= 0 && (!ent_list[index]->animation->vulnerable[ent_list[index]->animpos] || ent_list[index]->invincible & INVINCIBLE_INTANGIBLE))
23724            // don't turn to the one on the back
23725            || ((self->position.x < ent_list[i]->position.x) == (self->direction == DIRECTION_RIGHT) && diffd < diffo)
23726           )
23727         {
23728             index = i;
23729             diffo = diffd;
23730         }
23731 
23732 
23733     }
23734 
23735     if(index >= 0)
23736     {
23737         return ent_list[index];
23738     }
23739     return NULL;
23740 }
23741 
23742 //Used by default A.I. pattern
23743 // A.I. characters try to find a pickable item
normal_find_item()23744 entity *normal_find_item()
23745 {
23746 
23747     int i;
23748     int index = -1;
23749     entity *ce = NULL;
23750     //find the 'nearest' one
23751     for(i = 0; i < ent_max; i++)
23752     {
23753         ce = ent_list[i];
23754 
23755         if( ce->exists && normal_test_item(self, ce) )
23756         {
23757             if(index < 0 || diff(ce->position.x, self->position.x) + diff(ce->position.z, self->position.z) < diff(ent_list[index]->position.x, self->position.x) + diff(ent_list[index]->position.z, self->position.z))
23758             {
23759                 index = i;
23760             }
23761         }
23762     }
23763     if( index >= 0)
23764     {
23765         return ent_list[index];
23766     }
23767     return NULL;
23768 }
23769 
long_attack()23770 int long_attack()
23771 {
23772     return 0;
23773 }
23774 
melee_attack()23775 int melee_attack()
23776 {
23777     return 0;
23778 }
23779 
23780 // chose next attack in atchain, if succeeded, return 1, otherwise return 0.
perform_atchain()23781 int perform_atchain()
23782 {
23783     int pickanim = 0;
23784 
23785     if(self->modeldata.chainlength <= 0)
23786     {
23787         return 0;
23788     }
23789 
23790     if(self->combotime > _time)
23791     {
23792         self->combostep[0]++;
23793     }
23794     else
23795     {
23796         self->combostep[0] = 1;
23797     }
23798 
23799     if(self->modeldata.atchain[self->combostep[0] - 1] == 0) // 0 means the chain ends
23800     {
23801         self->combostep[0] = 1;
23802     }
23803 
23804     if(validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) )
23805     {
23806         if(((self->combostep[0] == 1 || !(self->modeldata.combostyle & 1)) && (self->modeldata.type & TYPE_PLAYER)) || // player should use attack 1st step without checking range
23807 
23808                 (!(self->modeldata.combostyle & 1) && normal_find_target(animattacks[self->modeldata.atchain[0] - 1], 0)) || // normal chain just checks the first attack in chain(guess no one like it)
23809 
23810                 ((self->modeldata.combostyle & 1) && normal_find_target(animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 0))) // combostyle 1 checks all anyway
23811         {
23812             pickanim = 1;
23813         }
23814         else if((self->modeldata.combostyle & 1) && self->combostep[0] != 1) // ranged combo? search for a valid attack
23815         {
23816 
23817             while(++self->combostep[0] <= self->modeldata.chainlength)
23818             {
23819                 if(self->modeldata.atchain[self->combostep[0] - 1] &&
23820                         validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) &&
23821                         (self->combostep[0] == self->modeldata.chainlength ||
23822                          normal_find_target(animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 0)))
23823                 {
23824                     pickanim = 1;
23825                     break;
23826                 }
23827             }
23828         }
23829     }
23830     else
23831     {
23832         self->combostep[0] = 0;
23833     }
23834     if(pickanim && validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]))
23835     {
23836         self->takeaction = common_attack_proc;
23837         set_attacking(self);
23838         ent_set_anim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 1);
23839     }
23840     if(!pickanim || self->combostep[0] > self->modeldata.chainlength)
23841     {
23842         self->combostep[0] = 0;
23843     }
23844     if((self->modeldata.combostyle & 2))
23845     {
23846         self->combotime = _time + combodelay;
23847     }
23848     return pickanim;
23849 }
23850 
upper_prepare()23851 void upper_prepare()
23852 {
23853     int predir = self->direction;
23854 
23855     entity *target = normal_find_target(ANI_UPPER, 0);
23856 
23857     self->velocity.x = self->velocity.z = 0; //stop
23858 
23859     if(!target)
23860     {
23861         self->idling = IDLING_PREPARED;
23862         self->takeaction = NULL;
23863         return;
23864     }
23865 
23866     //check if target is behind, so we can perform a turn back animation
23867     if(!self->modeldata.noflip)
23868     {
23869         self->direction = (self->position.x < target->position.x);
23870     }
23871     if(predir != self->direction && validanim(self, ANI_TURN))
23872     {
23873         self->takeaction = common_turn;
23874         self->direction = predir;
23875         set_turning(self);
23876         ent_set_anim(self, ANI_TURN, 0);
23877         return;
23878     }
23879 
23880     // Wait...
23881     if(_time < self->stalltime)
23882     {
23883         return;
23884     }
23885 
23886     // Target jumping? Try uppercut!
23887     if(target && target->jumping)
23888     {
23889         self->takeaction = common_attack_proc;
23890         set_attacking(self);
23891         self->velocity.z = self->velocity.x = 0;
23892         // Don't waste any time!
23893         ent_set_anim(self, ANI_UPPER, 0);
23894         return;
23895     }
23896 }
23897 
normal_prepare()23898 void normal_prepare()
23899 {
23900     int i, j;
23901     int found = 0, special = 0;
23902     int predir = self->direction;
23903 
23904     entity *target = normal_find_target(-1, 0);
23905 
23906     self->velocity.x = self->velocity.z = 0; //stop
23907 
23908     if(!target)
23909     {
23910         self->idling = IDLING_PREPARED;
23911         self->takeaction = NULL;
23912         return;
23913     }
23914 
23915     //check if target is behind, so we can perform a turn back animation
23916     if(!self->modeldata.noflip)
23917     {
23918         self->direction = (self->position.x < target->position.x);
23919     }
23920     if(predir != self->direction && validanim(self, ANI_TURN))
23921 
23922     {
23923         self->takeaction = common_turn;
23924         self->direction = predir;
23925         set_turning(self);
23926         ent_set_anim(self, ANI_TURN, 0);
23927         return;
23928     }
23929 
23930     // Wait...
23931     if(_time < self->stalltime)
23932     {
23933         return;
23934     }
23935     // let go the projectile, well
23936     if( self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
23937             validanim(self, ANI_THROWATTACK) &&
23938             check_range_target_all(self, target, ANI_THROWATTACK))
23939     {
23940         self->takeaction = common_attack_proc;
23941         set_attacking(self);
23942         ent_set_anim(self, ANI_THROWATTACK, 0);
23943         return ;
23944     }
23945 
23946     // move freespecial check here
23947 
23948     for(i = 0; i < max_freespecials; i++)
23949     {
23950         if(validanim(self, animspecials[i]) &&
23951                 (check_energy(COST_CHECK_MP, animspecials[i]) ||
23952                  check_energy(COST_CHECK_HP, animspecials[i])) &&
23953                 check_range_target_all(self, target, animspecials[i]))
23954         {
23955             atkchoices[found++] = animspecials[i];
23956         }
23957     }
23958     if((rand32() & 7) < 2)
23959     {
23960         if(found && check_costmove(atkchoices[(rand32() & 0xffff) % found], 1, 0) )
23961         {
23962             return;
23963         }
23964     }
23965     special = found;
23966 
23967     if(self->modeldata.chainlength > 1) // have a chain?
23968     {
23969         if(perform_atchain())
23970         {
23971             return;
23972         }
23973     }
23974     else if (self->ducking & DUCK_ACTIVE)
23975     {
23976         self->takeaction = common_attack_proc;
23977         set_attacking(self);
23978         ent_set_anim(self, ANI_DUCKATTACK, 0);
23979         return;
23980     }
23981     else // dont have a chain so just select an attack randomly
23982     {
23983         // Pick an attack
23984         for(i = 0; i < max_attacks; i++)
23985         {
23986             if( validanim(self, animattacks[i]) &&
23987                     check_range_target_all(self, target, animattacks[i]))
23988             {
23989                 // a trick to make attack 1 has a greater chance to be chosen
23990                 // 6 5 4 3 2 1 1 1 1 1 ....
23991                 for(j = ((5 - i) >= 0 ? (5 - i) : 0); j >= 0; j--)
23992                 {
23993                     atkchoices[found++] = animattacks[i];
23994                 }
23995             }
23996         }
23997         if(found > special)
23998         {
23999             self->takeaction = common_attack_proc;
24000             set_attacking(self);
24001             ent_set_anim(self, atkchoices[special + (rand32() & 0xffff) % (found - special)], 0);
24002             return;
24003         }
24004     }
24005 
24006     // if no attack was picked, just choose a random one from the valid list
24007     if(special && check_costmove(atkchoices[(rand32() & 0xffff) % special], 1, 0))
24008     {
24009         return;
24010     }
24011 
24012     // No attack to perform, return to A.I. root
24013     self->idling = IDLING_PREPARED;
24014     self->takeaction = NULL;
24015 }
24016 
common_jumpland()24017 void common_jumpland()
24018 {
24019     if(self->animating)
24020     {
24021         return;
24022     }
24023     self->takeaction = NULL;
24024     set_idle(self);
24025 }
24026 
24027 //A.I characters play the jump animation
common_jump()24028 void common_jump()
24029 {
24030     entity *dust;
24031 
24032     if(inair(self))
24033     {
24034         //printf("%f %f %f %d\n", self->base, self->position.y, self->velocity.y, self->landed_on_platform);
24035         return;
24036     }
24037 
24038     if(self->velocity.y <= 0) // wait if it is still go up
24039     {
24040         self->velocity.y = 0;
24041         self->position.y = self->base;
24042 
24043         self->jumping = 0;
24044         self->ducking = DUCK_NONE;
24045         self->attacking = ATTACKING_NONE;
24046 
24047         if(!self->modeldata.runhold)
24048         {
24049             self->running = 0;
24050         }
24051 
24052         self->velocity.z = self->velocity.x = 0;
24053 
24054         // check if jumpland animation exists and not using landframe
24055         if(validanim(self, ANI_JUMPLAND) && !self->animation->landframe)
24056         {
24057             self->takeaction = common_jumpland;
24058             ent_set_anim(self, ANI_JUMPLAND, 0);
24059             if(self->modeldata.dust.jump_land >= 0)
24060             {
24061                 dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_land, NULL);
24062                 if(dust)
24063                 {
24064                     dust->spawntype = SPAWN_TYPE_DUST_LAND;
24065                     dust->base = self->position.y;
24066                     dust->autokill |= AUTOKILL_ANIMATION_COMPLETE;
24067                     execute_onspawn_script(dust);
24068                 }
24069             }
24070         }
24071         else
24072         {
24073             if(self->modeldata.dust.jump_land >= 0 && !self->animation->landframe)
24074             {
24075                 dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_land, NULL);
24076                 if(dust)
24077                 {
24078                     dust->spawntype = SPAWN_TYPE_DUST_LAND;
24079                     dust->base = self->position.y;
24080                     dust->autokill |= AUTOKILL_ANIMATION_COMPLETE;
24081                     execute_onspawn_script(dust);
24082                 }
24083             }
24084             if(self->animation->landframe && self->animating)
24085             {
24086                 return;
24087             }
24088 
24089             self->takeaction = NULL; // back to A.I. root
24090             set_idle(self);
24091         }
24092     }
24093 }
24094 
24095 //A.I. characters spawn
common_spawn()24096 void common_spawn()
24097 {
24098     self->idling = IDLING_NONE;
24099     if(self->animating)
24100     {
24101         return;
24102     }
24103     self->takeaction = NULL; // come to life
24104     set_idle(self);
24105 }
24106 
24107 //A.I. characters drop from the sky
common_drop()24108 void common_drop()
24109 {
24110     if(inair(self))
24111     {
24112         return;
24113     }
24114     self->idling = IDLING_PREPARED;
24115     self->takeaction = NULL;
24116     if(self->energy_state.health_current <= 0)
24117     {
24118         kill_entity(self);
24119     }
24120 }
24121 
24122 //Similar as above, walk off a wall/cliff
common_walkoff()24123 void common_walkoff()
24124 {
24125     if(inair(self) || self->animating)
24126     {
24127         return;
24128     }
24129     self->takeaction = NULL;
24130     set_idle(self);
24131 }
24132 
24133 // play turn animation and then flip
common_turn()24134 void common_turn()
24135 {
24136     if(!self->animating)
24137     {
24138         self->takeaction = NULL;
24139         self->velocity.x = self->velocity.z = 0;
24140         self->direction = !self->direction;
24141         set_idle(self);
24142     }
24143 }
24144 
24145 // switch to land animation, land safely
doland()24146 void doland()
24147 {
24148     self->velocity.x = self->velocity.z = 0;
24149     self->drop = 0;
24150     self->projectile = BLAST_NONE;
24151     self->damage_on_landing.attack_force = 0;
24152     self->damage_on_landing.attack_type = ATK_NONE;
24153     if(validanim(self, ANI_LAND))
24154     {
24155         self->takeaction = common_land;
24156         self->direction = !self->direction;
24157         ent_set_anim(self, ANI_LAND, 0);
24158     }
24159     else
24160     {
24161         self->takeaction = NULL;
24162         set_idle(self);
24163     }
24164 }
24165 
common_fall()24166 void common_fall()
24167 {
24168     // Still falling?
24169     if(self->falling || inair(self) || self->velocity.y)
24170     {
24171         return;
24172     }
24173 
24174     // Landed. Let's see if we could land
24175 	// safely.
24176     if(self->projectile != BLAST_NONE)
24177 	{
24178 		if (self->projectile & BLAST_TOSS)
24179 		{
24180 			// damage_on_landing.attack_force==-2 means a player has pressed up+jump and has a land animation
24181 			if ((autoland == 1 && self->damage_on_landing.attack_force == ATTACK_FORCE_LAND_AUTO) || self->damage_on_landing.attack_force == ATTACK_FORCE_LAND_COMMAND)
24182 			{
24183 				// Added autoland option for landing
24184 				doland();
24185 				return;
24186 			}
24187 
24188 			self->falling = 0;
24189 		}
24190     }
24191 
24192     // Drop Weapon due to Enemy Falling.
24193     //if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_KNOCKDOWN) dropweapon(1);
24194 
24195     if(self->boss && level_completed)
24196     {
24197         tospeedup = 1;
24198     }
24199 
24200     // Pause a bit...
24201     self->takeaction	= common_lie;
24202     self->stalltime		= _time + MAX(0, (int)(self->staydown.rise + GAME_SPEED - self->modeldata.risetime.rise));	//Set rise delay.
24203     self->staydown.riseattack_stall	= _time + MAX(0, (int)(self->staydown.riseattack - self->modeldata.risetime.riseattack));					//Set rise attack delay.
24204     self->staydown.rise = 0; //Reset staydown.
24205     self->staydown.riseattack = 0; //Reset staydown atk.
24206 }
24207 
common_try_riseattack()24208 void common_try_riseattack()
24209 {
24210     entity *target;
24211     if(!validanim(self, ANI_RISEATTACK))
24212     {
24213         return;
24214     }
24215 
24216     target = normal_find_target(ANI_RISEATTACK, 0);
24217     if(!target)
24218     {
24219         self->direction = !self->direction;
24220         target = normal_find_target(ANI_RISEATTACK, 0);
24221         self->direction = !self->direction;
24222     }
24223 
24224     if(target)
24225     {
24226         self->direction = (target->position.x > self->position.x);    // Stands up and swings in the right direction depending on chosen target
24227         set_riseattack(self, self->last_damage_type, 0);
24228     }
24229 }
24230 
common_lie()24231 void common_lie()
24232 {
24233     // Died?
24234     if(self->energy_state.health_current <= 0)
24235     {
24236         if(self->modeldata.falldie == 2)
24237         {
24238             set_death(self, self->last_damage_type, 0);
24239         }
24240         if(!self->modeldata.nodieblink || (self->modeldata.nodieblink == 1 && !self->animating))
24241         {
24242             // Now have the option to blink or not
24243             self->takeaction = (self->modeldata.type & TYPE_PLAYER) ? player_blink : suicide;
24244             self->blink = 1;
24245             self->stalltime  = _time + GAME_SPEED * 2;
24246         }
24247         else if(self->modeldata.nodieblink == 2  && !self->animating)
24248         {
24249             self->takeaction = (self->modeldata.type & TYPE_PLAYER) ? player_die : suicide;
24250 
24251         }
24252         else if(self->modeldata.nodieblink == 3  && !self->animating)
24253         {
24254             if(self->modeldata.type & TYPE_PLAYER)
24255             {
24256                 self->takeaction = player_die;
24257 
24258             }
24259             else
24260             {
24261                 self->modeldata.type = TYPE_NONE;
24262                 self->noaicontrol = 1;
24263             }
24264         }
24265 
24266         if (self->modeldata.maps.ko)   //Have a KO map?
24267         {
24268             if (self->modeldata.maps.kotype == KOMAP_TYPE_IMMEDIATELY || !self->animating)  //Wait for fall/death animation to finish?
24269             {
24270                 self->colourmap = model_get_colourmap(&(self->modeldata), self->modeldata.maps.ko);    //Apply map.
24271             }
24272         }
24273 
24274         return;
24275     }
24276 
24277     if(_time < self->stalltime || self->position.y != self->base || self->velocity.y)
24278     {
24279         return;
24280     }
24281 
24282     set_rise(self, self->last_damage_type, 0);
24283 }
24284 
24285 // rise proc
common_rise()24286 void common_rise()
24287 {
24288     if(self->animating)
24289     {
24290         return;
24291     }
24292     self->takeaction = NULL;
24293     self->staydown.riseattack_stall = 0;	//Reset riseattack delay.
24294     if(self->modeldata.riseinv)
24295     {
24296         self->blink = self->modeldata.riseinv > 0;
24297         self->invinctime = _time + ABS(self->modeldata.riseinv);
24298         self->invincible |= INVINCIBLE_INTANGIBLE;
24299     }
24300     set_idle(self);
24301 }
24302 
24303 // pain proc
common_pain()24304 void common_pain()
24305 {
24306     //self->velocity.x = self->velocity.z = 0; // complained
24307 
24308     if(self->animating || inair(self))
24309     {
24310         return;
24311     }
24312 
24313     self->inpain = 0;
24314     self->rising = RISING_NONE;
24315     self->ducking = DUCK_NONE;
24316     self->inbackpain = 0;
24317     if(self->link)
24318     {
24319 //        set_pain(self, -1, 0);
24320         self->takeaction = common_grabbed;
24321     }
24322     else if(self->blocking)
24323     {
24324         self->takeaction = common_block;
24325         ent_set_anim(self, ANI_BLOCK, 1);
24326     }
24327     else
24328     {
24329         self->takeaction = NULL;
24330         set_idle(self);
24331     }
24332 }
24333 
doprethrow()24334 void doprethrow()
24335 {
24336     entity *other = self->link;
24337     other->takeaction = common_prethrow;
24338     self->takeaction = common_throw_wait;
24339     self->velocity.x = self->velocity.z = self->velocity.y = other->velocity.x = other->velocity.z = other->velocity.y = 0;
24340     ent_set_anim(self, ANI_THROW, 0);
24341 }
24342 
24343 // 1 grabattack 2 grabforward 3 grabup 4 grabdown 5 grabbackward
24344 // other means grab finisher at once
24345 
24346 // Unknown author (utunnels?).
24347 //
24348 // Retooled by Caskey, Damon V. to use named constants
24349 // for selecting which grab attack.
24350 // 2019-05-31
24351 //
24352 // Perform a grab attack action depending on request and
24353 // current number already performed for of a given
24354 // grab attack.
dograbattack(int which)24355 void dograbattack(int which)
24356 {
24357     entity *other = self->link;
24358     self->takeaction = common_grabattack;
24359     self->attacking = ATTACKING_ACTIVE;
24360     other->velocity.x = other->velocity.z = self->velocity.x = self->velocity.z = 0;
24361 
24362 	// If we requested finish attack, do it now. Otherwise
24363 	// we'll look at current combostep for the selected grab
24364 	// attack. If we're at the combo limit, then we finish.
24365 	// If not, do the requested attack.
24366 	if (which == GRAB_ACTION_SELECT_FINISH)
24367 	{
24368 		do_grab_attack_finish(self, 0);
24369 	}
24370 	else
24371 	{
24372 		++self->combostep[which];
24373 		if (self->combostep[which] < 3 && validanim(self, grab_attacks[which][0]))
24374 		{
24375 			ent_set_anim(self, grab_attacks[which][0], 0);
24376 		}
24377 		else
24378 		{
24379 			do_grab_attack_finish(self, which);
24380 		}
24381 	}
24382 }
24383 
24384 // Caskey, Damon V.
24385 // 2018-04-11
24386 //
24387 // Choose appropriate grab finish animation
24388 // or do nothing if we can't find one. Returns
24389 // selected animation.
do_grab_attack_finish(entity * ent,int which)24390 e_animations do_grab_attack_finish(entity *ent, int which)
24391 {
24392     e_animations animation;
24393 
24394     // Clear out the combostep array since this is
24395     // the finishing attack.
24396     memset(ent->combostep, 0, sizeof(*ent->combostep) * 5);
24397 
24398     // Get the finisher animation.
24399     animation = grab_attacks[which][1];
24400 
24401     // If selected attack animation exists then
24402     // that's what we use. Otherwise default to
24403     // attack3. If THAT fails, we don't do anything.
24404     // The target entity is already unlinked from
24405     // grab before this function was called, so in
24406     // game they are let go with no finishing attack.
24407     if(validanim(ent, animation))
24408     {
24409         ent_set_anim(ent, animation, 0);
24410         return animation;
24411     }
24412     else if(validanim(ent, ANI_ATTACK3))
24413     {
24414         // Get the finisher animation.
24415         ent_set_anim(ent, ANI_ATTACK3, 0);
24416         return ANI_ATTACK3;
24417     }
24418 
24419     // Could not find a valid finisher. Return none.
24420     return ATK_NONE;
24421 }
24422 
common_grab_check()24423 void common_grab_check()
24424 {
24425     int rnum, which;
24426     entity *other = self->link;
24427 
24428     if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
24429     {
24430         return;
24431     }
24432 
24433     if(self->base != other->base)
24434     {
24435         // Change this from ->position.y to ->base
24436         self->takeaction = NULL;
24437         ent_unlink(self);
24438         set_idle(self);
24439         return;
24440     }
24441 
24442     if(!nolost && self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
24443     {
24444         dropweapon(1);
24445     }
24446 
24447     self->attacking = ATTACKING_NONE; //for checking
24448 
24449     rnum = rand32() & 31;
24450 
24451     if(_time > self->releasetime)
24452     {
24453         if(rnum < 12)
24454         {
24455             // Release
24456             self->takeaction = NULL;
24457             ent_unlink(self);
24458             set_idle(self);
24459             return;
24460         }
24461         else
24462         {
24463             self->releasetime = _time + (GAME_SPEED / 2);
24464         }
24465     }
24466 
24467     if(validanim(self, ANI_THROW) && rnum < 7)
24468     {
24469         if(self->modeldata.throwframewait >= 0)
24470         {
24471             doprethrow();
24472         }
24473         else
24474         {
24475             dothrow();
24476         }
24477         return;
24478     }
24479     //grab finisher
24480     if(rnum < 4)
24481     {
24482         dograbattack(GRAB_ACTION_SELECT_FINISH);
24483         return;
24484     }
24485     which = rnum % GRAB_ACTION_SELECT_MAX;
24486     // grab attacks
24487     if(rnum > 12 && validanim(self, grab_attacks[which][0]))
24488     {
24489         dograbattack(which);
24490         return;
24491     }
24492 }
24493 
24494 //grabbing someone
common_grab()24495 void common_grab()
24496 {
24497     // if(self->link) return;
24498     if(self->link || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
24499     {
24500         return;
24501     }
24502 
24503     self->takeaction = NULL;
24504     self->attacking = ATTACKING_NONE;
24505     memset(self->combostep, 0, sizeof(*self->combostep) * 5);
24506     set_idle(self);
24507 }
24508 
24509 // being grabbed
common_grabbed()24510 void common_grabbed()
24511 {
24512     // Just check if we're still grabbed...
24513     if(self->link)
24514     {
24515         return;
24516     }
24517 
24518     self->stalltime = 0;
24519     self->takeaction = NULL;
24520     set_idle(self);
24521 }
24522 
24523 // picking up something
common_get()24524 void common_get()
24525 {
24526     if(self->animating)
24527     {
24528         return;
24529     }
24530 
24531     self->getting = 0;
24532     self->takeaction = NULL;
24533     set_idle(self);
24534 }
24535 
24536 // Continue or release block.
common_block()24537 void common_block()
24538 {
24539 	// Player type with holdblock, also not in pain
24540 	// or has post blockpain holdblock ability.
24541     int hb1 = self->modeldata.holdblock
24542 		&& (self->modeldata.type & TYPE_PLAYER)
24543 		&& (!self->inpain || (self->modeldata.holdblock & 2));
24544 
24545 	// Controlling player is holding special key.
24546 	int hb2 = ((player + self->playerindex)->keys & FLAG_SPECIAL);
24547 
24548 	// If we are in a block transition, let's see if it is finished.
24549 	// If it is, apply block animation.
24550 	if (self->animnum == ANI_BLOCKSTART && !self->animating)
24551 	{
24552 		ent_set_anim(self, ANI_BLOCK, 0);
24553 	}
24554 
24555 	// In "Blockstun", at last frame of animation, and have holdblock
24556 	// after blockpain ability? Then we return to block.
24557 	//
24558 	// Otherwise, entity is a player with various other flags (see bh1) but
24559 	// not holding special key, or the entity has finihsed animation and
24560 	// doesn't match any of the player/holdblock criteria (could be another
24561 	// entity type, doesn't have holdblock ability, or controlling
24562 	// player isn't holding special key). In any of those cases, we disable
24563 	// blocking flag and return to idle.
24564     if(self->inpain
24565 		&& (self->modeldata.holdblock & 2)
24566 		&& !self->animating
24567 		&& validanim(self, ANI_BLOCK))
24568     {
24569 		self->inpain = 0;
24570 		self->rising = RISING_NONE;
24571 		self->inbackpain = 0;
24572 		ent_set_anim(self, ANI_BLOCK, 0);
24573     }
24574     else if((hb1 && !hb2)
24575 		|| (!self->animating && (!hb1 || !hb2)))
24576     {
24577 		// Can't release block until pain flag
24578 		// disables or the animation is complete. This
24579 		// forces blockpain animations to finish before
24580 		// entity can act again.
24581 		if (!self->inpain || !self->animating)
24582 		{
24583 			if (self->animnum == ANI_BLOCKRELEASE && !self->animating)
24584 			{
24585 				self->blocking = 0;
24586 				self->takeaction = NULL;
24587 				set_idle(self);
24588 			}
24589 			else
24590 			{
24591 				if (validanim(self, ANI_BLOCKRELEASE))
24592 				{
24593 					ent_set_anim(self, ANI_BLOCKRELEASE, 0);
24594 				}
24595 				else
24596 				{
24597 					self->blocking = 0;
24598 					self->takeaction = NULL;
24599 					set_idle(self);
24600 				}
24601 			}
24602 		}
24603     }
24604 }
24605 
24606 
common_charge()24607 void common_charge()
24608 {
24609     if(self->animating)
24610     {
24611         return;
24612     }
24613 
24614     self->charging = 0;
24615     self->takeaction = NULL;
24616     set_idle(self);
24617 }
24618 
24619 // common code for entities hold an item
drop_item(entity * e)24620 entity *drop_item(entity *e)
24621 {
24622     s_spawn_entry p;
24623     entity *item;
24624     memset(&p, 0, sizeof(p));
24625 
24626     // Just to make sure there is an item so
24627     // we don't look for data on a NULL pointer.
24628     if(!e->item_properties)
24629     {
24630         return NULL;
24631     }
24632 
24633     p.index         = e->item_properties->index;
24634     p.item_properties.index = p.weaponindex = -1;
24635     strcpy(p.alias, e->item_properties->alias);
24636     p.position.y    = e->position.y + 0.01; // For check, or an enemy "item" will drop from the sky
24637     p.health[0]     = e->item_properties->health;
24638     p.alpha         = e->item_properties->alpha;
24639     p.colourmap     = e->item_properties->colorset;
24640     p.flip          = e->direction;
24641 
24642     item = smartspawn(&p);
24643 
24644     if(item)
24645     {
24646         item->spawntype = SPAWN_TYPE_ITEM;
24647 
24648         item->position.x = e->position.x;
24649         item->position.z = e->position.z;
24650         if(item->position.x < advancex)
24651         {
24652             item->position.x = advancex + 10;
24653         }
24654         else if(item->position.x > advancex + videomodes.hRes)
24655         {
24656             item->position.x = advancex + videomodes.hRes - 10;
24657         }
24658         if(!(level->scrolldir & (SCROLL_UP | SCROLL_DOWN)))
24659         {
24660             if(item->position.z - item->position.y < advancey)
24661             {
24662                 item->position.z = advancey + 10;
24663             }
24664             else if(item->position.z - item->position.y > advancey + videomodes.vRes)
24665             {
24666                 item->position.z = advancey + videomodes.vRes - 10;
24667             }
24668         }
24669         if(e->boss && (item->modeldata.type & TYPE_ENEMY))
24670         {
24671             item->boss = 1;
24672         }
24673     }
24674     return item;
24675 }
24676 
24677 //drop the driver, just spawn, dont takedamage
24678 // damage will adjust by the biker
drop_driver(entity * e)24679 entity *drop_driver(entity *e)
24680 {
24681     int i;
24682     s_spawn_entry p;
24683     entity *driver;
24684     memset(&p, 0, sizeof(p));
24685 
24686     if(e->modeldata.rider >= 0)
24687     {
24688         p.index = e->modeldata.rider;
24689     }
24690     else
24691     {
24692         return NULL;    // should not happen, just in case
24693     }
24694     p.position.y = e->position.y + 10;
24695     p.weaponindex = -1;
24696     strcpy(p.alias, e->name);
24697 
24698     // Carrying an item, so let's transfer that to spawn
24699     // entry for the driver.
24700     if(e->item_properties)
24701     {
24702         strcpy(p.item_properties.alias, e->item_properties->alias);
24703 
24704         p.item_properties.index         = e->item_properties->index;
24705         p.item_properties.colorset      = e->item_properties->colorset;
24706         p.item_properties.alpha         = e->item_properties->alpha;
24707         p.item_properties.health        = e->item_properties->health;
24708         p.item_properties.player_count  = e->item_properties->player_count;
24709     }
24710 
24711 
24712     //p.colourmap = e->map;
24713     for(i = 0; i < MAX_PLAYERS; i++)
24714     {
24715         p.health[i] = e->modeldata.health;
24716     }
24717     p.boss = e->boss;
24718 
24719     driver = smartspawn(&p);
24720     if(driver)
24721     {
24722         driver->spawntype = SPAWN_TYPE_BIKER;
24723         driver->position.x = e->position.x;
24724         driver->position.z = e->position.z;
24725     }
24726     return driver;
24727 }
24728 
24729 
checkdeath()24730 void checkdeath()
24731 {
24732     if(self->energy_state.health_current > 0)
24733     {
24734         return;
24735     }
24736     self->dead = 1;
24737     //be careful, since the opponent can be other types
24738     if(self->opponent && (self->opponent->modeldata.type & TYPE_PLAYER))
24739     {
24740         addscore(self->opponent->playerindex, self->modeldata.score);    // Add score to the player
24741     }
24742     self->nograb = 1;
24743     self->idling = IDLING_NONE;
24744     self->ducking = DUCK_NONE;
24745 
24746     if(self->modeldata.diesound >= 0)
24747     {
24748         sound_play_sample(self->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100);
24749     }
24750 
24751     // Drop an item if we have one.
24752     if(self->item_properties)
24753     {
24754         if(count_ents(TYPE_PLAYER) > self->item_properties->player_count)
24755         {
24756             drop_item(self);
24757         }
24758     }
24759 
24760 
24761     if(self->boss)
24762     {
24763         self->boss = 0;
24764         --level->bossescount;
24765         if(level->bossescount <= 0 && (self->modeldata.type & TYPE_ENEMY))
24766         {
24767             kill_all_enemies();
24768             level_completed = 1;
24769             level_completed_defeating_boss |= 1;
24770         }
24771     }
24772 }
24773 
checkdamageflip(entity * other,s_collision_attack * attack)24774 void checkdamageflip(entity *other, s_collision_attack *attack)
24775 {
24776     self->normaldamageflipdir = -1;
24777 
24778     if(other == NULL || other == self || (!self->drop && (attack->no_pain || self->modeldata.nopain || (self->defense[attack->attack_type].pain && attack->attack_force < self->defense[attack->attack_type].pain))))
24779     {
24780         return;
24781     }
24782 
24783     if(!self->frozen && !self->modeldata.noflip)// && !inair(self))
24784     {
24785         switch(attack->force_direction)
24786         {
24787             case DIRECTION_ADJUST_NONE:
24788                 if( !self->inbackpain )
24789                 {
24790                     if(self->position.x < other->position.x)
24791                     {
24792                         self->direction = DIRECTION_RIGHT;
24793                     }
24794                     else if(self->position.x > other->position.x)
24795                     {
24796                         self->direction = DIRECTION_LEFT;
24797                     }
24798                 }
24799                 else
24800                 {
24801                     if(self->position.x < other->position.x)
24802                     {
24803                         self->normaldamageflipdir = DIRECTION_RIGHT;
24804                     }
24805                     else if(self->position.x > other->position.x)
24806                     {
24807                         self->normaldamageflipdir = DIRECTION_LEFT;
24808                     }
24809                 }
24810                 break;
24811 
24812             case DIRECTION_ADJUST_SAME:
24813 
24814                 self->direction = other->direction;
24815                 break;
24816 
24817             case DIRECTION_ADJUST_OPPOSITE:
24818 
24819                 self->direction = !other->direction;
24820                 break;
24821 
24822             case DIRECTION_ADJUST_RIGHT:
24823 
24824                 self->direction = DIRECTION_RIGHT;
24825                 break;
24826 
24827             case DIRECTION_ADJUST_LEFT:
24828 
24829                 self->direction = DIRECTION_LEFT;
24830                 break;
24831         }
24832     }
24833 }
24834 
checkdamageeffects(s_collision_attack * attack)24835 void checkdamageeffects(s_collision_attack *attack)
24836 {
24837 #define _freeze         attack->freeze
24838 #define _maptime        attack->maptime
24839 #define _freezetime     attack->freezetime
24840 #define _remap          attack->forcemap
24841 #define _blast          attack->blast
24842 #define _steal          attack->steal
24843 #define _seal           attack->seal
24844 #define _sealtime       attack->sealtime
24845 #define _staydown_rise			attack->staydown.rise
24846 #define _staydown_rise_attack	attack->staydown.riseattack
24847 
24848     entity *opp = self->opponent;
24849 
24850 	// Steal. Take HP from the entity and add it to attacker.
24851     if(_steal && opp && opp != self)
24852     {
24853 		// If we have enough HP to withstand the attack, give attacker
24854 		// the same amount as attack force. Otherwise just give them
24855 		// whatever HP we have left.
24856 		if(self->energy_state.health_current >= attack->attack_force)
24857         {
24858             opp->energy_state.health_current += attack->attack_force;
24859         }
24860         else
24861         {
24862             opp->energy_state.health_current += self->energy_state.health_current;
24863         }
24864 
24865 		// Cap the effect so attacker doesn't go over their maximum HP.
24866         if(opp->energy_state.health_current > opp->modeldata.health)
24867         {
24868             opp->energy_state.health_current = opp->modeldata.health;
24869         }
24870     }
24871 
24872 	// Freeze effect. If this is a freeze attack and we're
24873 	// not already frozen, apply a freeze effect and possibly
24874 	// remap to freeze palette. If we ARE frozen, then
24875 	// unfreeze and knock down instead.
24876     if(_freeze && !self->frozen)
24877     {
24878 
24879 		// Set freeze status and expire time.
24880         self->frozen = 1;
24881         if(self->freezetime == 0)
24882         {
24883             self->freezetime = _time + _freezetime;
24884         }
24885 
24886 		// 2007-12-14
24887 		// Caskey, Damon V.
24888 		//
24889 		// If opponents frozen map = -1 or only stun, then don't change the color map.
24890 
24891         if(_remap == -1 && self->modeldata.maps.frozen != -1)
24892         {
24893             self->colourmap = model_get_colourmap(&(self->modeldata), self->modeldata.maps.frozen);
24894         }
24895 
24896         self->drop = 0;
24897     }
24898     else if(self->frozen)
24899     {
24900         unfrozen(self);
24901         self->drop = 1;
24902     }
24903 
24904 	// If we want to apply a remap without freezing (forcemap attack command) then
24905 	// set the map and expire time here.
24906     if(_remap > 0 && !_freeze)
24907     {
24908         self->maptime = _time + _maptime;
24909         self->colourmap = model_get_colourmap(&(self->modeldata), _remap);
24910     }
24911 
24912 	// Disable specials. Apply seal (Any animation with
24913 	// energycost > seal) is disabled and time to expire.
24914     if(_seal)
24915     {
24916         self->sealtime  = _time + _sealtime;
24917         self->seal      = _seal;
24918     }
24919 
24920 	// Apply any recursive (damage over time) effects.
24921 	check_damage_recursive(self, opp, attack);
24922 
24923 	// Static enemies/nodrop enemies cannot be knocked down
24924     if(self->modeldata.nodrop)
24925     {
24926         self->drop = 0;
24927     }
24928 
24929 	// Always knock airborne entities down unless we're freezeing them
24930 	// or they are specfically immune to in air knockdowns.
24931     if(inair(self) && !self->frozen && self->modeldata.nodrop < 2)
24932     {
24933         self->drop = 1;
24934     }
24935 
24936 	// Immune to hit stun? No knockdown either.
24937     if(attack->no_pain)
24938     {
24939         self->drop = 0;
24940     }
24941 
24942 	// If entity will be knocked down, let's apply knockdown specific effects here.
24943     if(self->drop)
24944     {
24945 		self->projectile = _blast;
24946 
24947         self->staydown.rise	= _staydown_rise;                                            //Staydown: Add to risetime until next rise.
24948         self->staydown.riseattack   = _staydown_rise_attack;
24949     }
24950 
24951 #undef _freeze
24952 #undef _maptime
24953 #undef _freezetime
24954 #undef _remap
24955 #undef _blast
24956 #undef _steal
24957 #undef _seal
24958 #undef _sealtime
24959 #undef _staydown_rise
24960 #undef _staydown_rise_attack
24961 }
24962 
24963 // Caskey, Damon V.
24964 // 2019-01-15
24965 //
24966 // If attack has any recursive effects, apply
24967 // them to entity accordingly.
check_damage_recursive(entity * ent,entity * other,s_collision_attack * attack)24968 void check_damage_recursive(entity *ent, entity *other, s_collision_attack *attack)
24969 {
24970 	s_damage_recursive *previous;
24971 	s_damage_recursive *cursor;
24972 
24973 	// If the recursive head pointer is
24974 	// null, there's no recursive, so exit.
24975 	if (!attack->recursive)
24976 	{
24977 		return;
24978 	}
24979 
24980 	// Let's see if we have a allocated any elements
24981 	// for recursive damage already.
24982 	if (ent->recursive_damage)
24983 	{
24984 		// Iterate over linked list and try to find an index
24985 		// member matching index member from attack. If we
24986 		// find one, exit loop - we can use the target pointer.
24987 		//
24988 		// If we don't find a match, we'll need to create a new
24989 		// node in the list and its pointer instead.
24990 
24991 		cursor = ent->recursive_damage;
24992 
24993 		while (cursor != NULL)
24994 		{
24995 			previous = cursor;
24996 
24997 			// Found index match, so we can use the cursor
24998 			// as is. Get out now.
24999 			if (cursor->index == attack->recursive->index)
25000 			{
25001 				break;
25002 			}
25003 
25004 			// Move to next node in list (if any).
25005 			cursor = cursor->next;
25006 		}
25007 
25008 		// Add new node to list.
25009 		if (!cursor)
25010 		{
25011 			// Allocate the memory and get pointer.
25012 			cursor = malloc(sizeof(*cursor));
25013 
25014 			// Make sure there's no random garbage in our next pointer.
25015 			cursor->next = NULL;
25016 
25017 			// Link previous node's next to our new node.
25018 			previous->next = cursor;
25019 		}
25020 	}
25021 	else
25022 	{
25023 		// Entity didn't have recursive damage at all.
25024 		// Let's allocate a head node.
25025 
25026 		// Allocate the memory and get pointer.
25027 		cursor = malloc(sizeof(*cursor));
25028 
25029 		// Make sure there's no random garbage in our next pointer.
25030 		cursor->next = NULL;
25031 
25032 		// Assign to entity.
25033 		ent->recursive_damage = cursor;
25034 	}
25035 
25036 	// Now we have a target recursive element to populate with
25037 	// attack's recursive values.
25038 	cursor->tag = attack->recursive->tag;
25039 	cursor->mode = attack->recursive->mode;
25040 	cursor->index = attack->recursive->index;
25041 	cursor->time = _time + (attack->recursive->time * GAME_SPEED / 100);
25042 	cursor->force = attack->recursive->force;
25043 	cursor->rate = attack->recursive->rate;
25044 	cursor->type = attack->attack_type;
25045 	cursor->owner = other;
25046 }
25047 
checkdamagedrop(s_collision_attack * attack)25048 void checkdamagedrop(s_collision_attack *attack)
25049 {
25050     int attackdrop = attack->attack_drop;
25051     float fdefense_knockdown = self->defense[attack->attack_type].knockdown;
25052     if(self->modeldata.animal)
25053     {
25054         self->drop = 1;
25055     }
25056     if(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0)
25057     {
25058         attackdrop = 0;    //guardbreak does not knock down.
25059     }
25060     if(self->drop || attack->no_pain)
25061     {
25062         return;    // just in case, if we already fall, dont check fall again
25063     }
25064     // reset count if knockdowntime expired.
25065     if(self->knockdowntime && self->knockdowntime < _time)
25066     {
25067         self->knockdowncount = self->modeldata.knockdowncount;
25068     }
25069 
25070     self->knockdowncount -= (attackdrop * fdefense_knockdown);
25071     self->knockdowntime = _time + GAME_SPEED;
25072     self->drop = (self->knockdowncount < 0); // knockdowncount < 0 means knocked down
25073 }
25074 
checkmpadd()25075 void checkmpadd()
25076 {
25077     entity *other = self->opponent;
25078     if(other == NULL || other == self)
25079     {
25080         return;
25081     }
25082 
25083     if(magic_type == 1 )
25084     {
25085         other->energy_state.mp_current += other->modeldata.mprate;
25086 
25087         if(other->energy_state.mp_current > other->modeldata.mp)
25088         {
25089             other->energy_state.mp_current = other->modeldata.mp;
25090         }
25091         else if(other->energy_state.mp_current < 0)
25092         {
25093             other->energy_state.mp_current = 0;
25094         }
25095     }
25096 }
25097 
checkhitscore(entity * other,s_collision_attack * attack)25098 void checkhitscore(entity *other, s_collision_attack *attack)
25099 {
25100     entity *opp = self->opponent;
25101     if(!opp)
25102     {
25103         return;
25104     }
25105     if(opp && opp != self && (opp->modeldata.type & TYPE_PLAYER))
25106     {
25107         // Added obstacle so explosions can hurt enemies
25108         addscore(opp->playerindex, attack->attack_force * self->modeldata.multiple);  // New multiple variable
25109         if (savedata.joyrumble[opp->playerindex]) control_rumble(opp->playerindex, 1, attack->attack_force * 2);
25110     }
25111     // Don't animate or fall if hurt by self, since
25112     // it means self fell to the ground already. :)
25113     // Add throw score to the player
25114     else if(other == self && self->damage_on_landing.attack_force > 0)
25115     {
25116         addscore(opp->playerindex, attack->attack_force);
25117     }
25118 }
25119 
calculate_force_damage(entity * other,s_collision_attack * attack)25120 int calculate_force_damage(entity *other, s_collision_attack *attack)
25121 {
25122     int force = attack->attack_force;
25123     int type = attack->attack_type;
25124 
25125     if(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0)
25126     {
25127         force = 0;    //guardbreak does not deal damage.
25128     }
25129     if(type >= 0 && type < max_attack_types)
25130     {
25131         force = (int)(force * other->offense_factors[type]);
25132         force = (int)(force * self->defense[type].factor);
25133     }
25134 
25135     return force;
25136 }
25137 
checkdamageonlanding()25138 void checkdamageonlanding()
25139 {
25140     if (self->energy_state.health_current <= 0) return;
25141 
25142     if( (self->damage_on_landing.attack_force > 0 && !self->dead) )
25143     {
25144         int didhit = 0;
25145 
25146         //##################
25147         s_collision_attack attack;
25148         entity *other;
25149 
25150         attack              = emptyattack;
25151         attack.attack_force = self->damage_on_landing.attack_force;
25152         if (attack.damage_on_landing.attack_type >= 0) attack.attack_type  = self->damage_on_landing.attack_type;
25153         else attack.attack_type  = ATK_LAND;
25154 
25155         if (self->opponent && self->opponent->exists && !self->opponent->dead && self->opponent->energy_state.health_current > 0) other = self->opponent;
25156         else other = self;
25157 
25158         lasthit.confirm = 1;
25159         lasthit.attack = &attack;
25160         lasthit.position.x = self->position.x;
25161         lasthit.position.y = self->position.y;
25162         lasthit.position.z = self->position.z;
25163 
25164         // Execute the doattack functions before damage is
25165         // processed.
25166         execute_ondoattack_script(self, other, &attack, EXCHANGE_RECIPIANT, other->attack_id_outgoing);
25167         execute_ondoattack_script(other, self, &attack, EXCHANGE_CONFERRER, other->attack_id_outgoing);
25168 
25169         if(lasthit.confirm)
25170         {
25171             didhit = 1;
25172         }
25173 
25174         if(self->dead)
25175         {
25176             return;
25177         }
25178         if(self->toexplode & EXPLODE_DETONATE)
25179         {
25180             return;
25181         }
25182         // fake 'grab', if failed, return as the attack hit nothing
25183         if(!checkgrab(other, &attack))
25184         {
25185             return;    // try to grab but failed, so return 0 means attack missed
25186         }
25187 
25188         if(self != other)
25189         {
25190             set_opponent(self, other);
25191         }
25192         //##################
25193 
25194         if (didhit)
25195         {
25196             // pre-check drop
25197             checkdamagedrop(&attack);
25198             // Drop Weapon due to being hit.
25199             if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
25200             {
25201                 dropweapon(1);
25202             }
25203             // check effects, e.g., frozen, blast, steal
25204             if(!(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0))
25205             {
25206                 checkdamageeffects(&attack);
25207             }
25208 
25209             // mprate can also control the MP recovered per hit.
25210             checkmpadd();
25211             //damage score
25212             checkhitscore(other, &attack);
25213 
25214             checkdamage(other, &attack);
25215 
25216             // is it dead now?
25217             checkdeath();
25218 
25219             execute_didhit_script(other, self, &attack, 0);
25220         }
25221 
25222         if (self->energy_state.health_current <= 0)
25223         {
25224             self->die_on_landing = 1;
25225         }
25226 
25227         self->damage_on_landing.attack_force = 0;
25228     }
25229 
25230     // takedamage if thrown or basted
25231     //if( (self->damage_on_landing.attack_force > 0 && !self->dead) &&
25232     if( (self->die_on_landing && !self->dead) &&
25233         ((!tobounce(self) && self->modeldata.bounce) || !self->modeldata.bounce) &&
25234         (self->velocity.x == 0 && self->velocity.z == 0 && self->velocity.y == 0)
25235       )
25236     {
25237         if(self->takedamage)
25238         {
25239             //##################
25240             s_collision_attack attack;
25241             entity *other;
25242 
25243             attack              = emptyattack;
25244             attack.attack_force = self->damage_on_landing.attack_force;
25245             if (attack.damage_on_landing.attack_type >= 0) attack.attack_type  = self->damage_on_landing.attack_type;
25246             else attack.attack_type  = ATK_LAND;
25247 
25248             if (self->opponent && self->opponent->exists && !self->opponent->dead && self->opponent->energy_state.health_current > 0) other = self->opponent;
25249             else other = self;
25250             //##################
25251 
25252             self->takedamage(other, &attack, 1);
25253         }
25254         else
25255         {
25256             kill_entity(self);
25257         }
25258         if (self)
25259         {
25260             self->damage_on_landing.attack_force = 0;
25261             self->damage_on_landing.attack_type = ATK_NONE;
25262             self->die_on_landing = 0;
25263         }
25264     }
25265 
25266     return;
25267 }
25268 
checkdamage(entity * other,s_collision_attack * attack)25269 void checkdamage(entity *other, s_collision_attack *attack)
25270 {
25271 	int		force;
25272 	bool	normal_damage;
25273 
25274 	// Get attack damage force after defense is applied.
25275     force = calculate_force_damage(other, attack);
25276 
25277 	// Damage does not return HP and comes from
25278 	// a normal source?
25279 	normal_damage = (attack->attack_type != ATK_BOSS_DEATH
25280 		&& attack->attack_type != ATK_ITEM
25281 		&& attack->attack_type != ATK_LIFESPAN
25282 		&& attack->attack_type != ATK_LOSE
25283 		&& attack->attack_type != ATK_TIMEOVER
25284 		&& attack->attack_type != ATK_PIT
25285 		&& force >= 0);
25286 
25287 	// If we're invincible to normal damage sources, laugh it off.
25288 	if (self->invincible & INVINCIBLE_HP_NULLIFY && normal_damage)
25289 	{
25290 		force = 0;
25291 	}
25292 
25293 	// Apply damage.
25294     self->energy_state.health_current -= force;
25295 
25296 	// Cap negative damage (giving back HP) to maximum health.
25297     if (self->energy_state.health_current > self->modeldata.health)
25298     {
25299         self->energy_state.health_current = self->modeldata.health;    //Cap negative damage to max health.
25300     }
25301 
25302     if(attack->no_kill && self->energy_state.health_current <= 0)
25303     {
25304         self->energy_state.health_current = 1;
25305     }
25306 
25307     // Execute the take damage script.
25308     execute_takedamage_script(self, other, attack);
25309 
25310 	// Attack meant to put health at 0?
25311     if (self->energy_state.health_current <= 0)
25312     {
25313 		// Normal attack source?
25314         if(normal_damage)
25315         {
25316 			// Stop at minium HP?
25317             if (self->invincible & INVINCIBLE_HP_MINIMUM)
25318             {
25319                 self->energy_state.health_current = 1;
25320             }
25321 
25322 			// Reset to maximum HP?
25323 			if(self->invincible & INVINCIBLE_HP_RESET)
25324             {
25325                 self->energy_state.health_current = self->modeldata.health;
25326             }
25327         }
25328 
25329         // Execute ondeath script.
25330         execute_ondeath_script(self, other, attack);
25331     }
25332 
25333     return;
25334 }
25335 
checkgrab(entity * other,s_collision_attack * attack)25336 int checkgrab(entity *other, s_collision_attack *attack)
25337 {
25338     //if(attack->no_pain) return  0; //no effect, let modders to deside, don't bother check it here
25339     if(self != other && attack->grab && cangrab(other, self))
25340     {
25341         if(adjust_grabposition(other, self, attack->grab_distance, attack->grab))
25342         {
25343             ents_link(other, self);
25344             self->position.y = other->position.y;
25345         }
25346         else
25347         {
25348             return 0;
25349         }
25350     }
25351     return 1;
25352 }
25353 
arrow_takedamage(entity * other,s_collision_attack * attack,int fall_flag)25354 int arrow_takedamage(entity *other, s_collision_attack *attack, int fall_flag)
25355 {
25356     self->modeldata.no_adjust_base = 0;
25357     self->modeldata.subject_to_wall = self->modeldata.subject_to_platform = self->modeldata.subject_to_hole = self->modeldata.subject_to_basemap = self->modeldata.subject_to_gravity = 1;
25358     if( common_takedamage(other, attack, 0) && self->dead)
25359     {
25360         return 1;
25361     }
25362     return 0;
25363 }
25364 
common_takedamage(entity * other,s_collision_attack * attack,int fall_flag)25365 int common_takedamage(entity *other, s_collision_attack *attack, int fall_flag)
25366 {
25367     if(self->dead)
25368     {
25369         return 0;
25370     }
25371     if(self->toexplode & EXPLODE_DETONATE)
25372     {
25373         return 0;
25374     }
25375     // fake 'grab', if failed, return as the attack hit nothing
25376     if(!checkgrab(other, attack))
25377     {
25378         return 0;    // try to grab but failed, so return 0 means attack missed
25379     }
25380 
25381     // set next_hit_time so it wont get hit too often
25382     // 2011/11/24 UT: move this to do_attack to merge with block code
25383     //self->next_hit_time = _time + (attack->next_hit_time?attack->next_hit_time:(GAME_SPEED / 5));
25384     // set oppoent
25385     if(self != other)
25386     {
25387         set_opponent(self, other);
25388     }
25389 
25390     // adjust type
25391     if(attack->attack_type >= 0 && attack->attack_type < max_attack_types)
25392     {
25393         self->last_damage_type = attack->attack_type;
25394     }
25395     else
25396     {
25397         self->last_damage_type = ATK_NORMAL;
25398     }
25399 
25400     if (!self->die_on_landing)
25401     {
25402         // pre-check drop
25403         checkdamagedrop(attack);
25404         // Drop Weapon due to being hit.
25405         if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
25406         {
25407             dropweapon(1);
25408         }
25409         // check effects, e.g., frozen, blast, steal
25410         if(!(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0))
25411         {
25412             checkdamageeffects(attack);
25413         }
25414     }
25415 
25416     // check backpain
25417     check_backpain(other,self);
25418     // check flip direction
25419     checkdamageflip(other, attack);
25420 
25421     if (!self->die_on_landing)
25422     {
25423         // mprate can also control the MP recovered per hit.
25424         checkmpadd();
25425         //damage score
25426         checkhitscore(other, attack);
25427         // check damage, cost hp.
25428         checkdamage(other, attack);
25429         // is it dead now?
25430         checkdeath();
25431     }
25432 
25433     if(self->modeldata.type & TYPE_PLAYER)
25434     {
25435         if (savedata.joyrumble[self->playerindex]) control_rumble(self->playerindex, 1, attack->attack_force * 3);
25436     }
25437     if(self->position.y <= PIT_DEPTH && self->dead)
25438     {
25439         if(self->modeldata.type & TYPE_PLAYER)
25440         {
25441             player_die();
25442         }
25443         else
25444         {
25445             kill_entity(self);
25446         }
25447         return 1;
25448     }
25449 
25450     // fall to the ground so don't fall again
25451     /*if(self->damage_on_landing.attack_force)
25452     {
25453         self->damage_on_landing.attack_force = 0;
25454         return 1;
25455     }*/
25456     // reset damageonlanding
25457     self->damage_on_landing.attack_force = 0;
25458     self->damage_on_landing.attack_type = ATK_NONE;
25459 
25460 	// White Dragon: fix damage_on_landing bug
25461 	if(self->die_on_landing && self->energy_state.health_current <= 0)
25462 	{
25463 		self->modeldata.falldie = 1;
25464 	}
25465 
25466     // unlink due to being hit
25467     if((self->opponent && self->opponent->grabbing != self) // Have an opponent, but opponent is not grabbing me.
25468 		|| self->dead										// Dead.
25469 		|| self->frozen										// Frozen.
25470 		|| self->drop)										// Knocked down.
25471     {
25472         ent_unlink(self);
25473     }
25474     // Enemies can now use SPECIAL2 to escape cheap attack strings!
25475     if(self->modeldata.escapehits)
25476     {
25477         if(self->drop)
25478         {
25479             self->escapecount = 0;
25480         }
25481         else
25482         {
25483             self->escapecount++;
25484         }
25485     }
25486 
25487     // New pain, fall, and death animations. Also, the nopain flag.
25488     if(self->drop || self->energy_state.health_current <= 0)
25489     {
25490         self->takeaction = common_fall;
25491         // Drop Weapon due to death.
25492         if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_DEATH && self->energy_state.health_current <= 0)
25493         {
25494             dropweapon(1);
25495         }
25496         else if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_KNOCKDOWN)
25497         {
25498             dropweapon(1);
25499         }
25500 
25501         if(self->energy_state.health_current <= 0 && self->modeldata.falldie == 1)
25502         {
25503             self->velocity.x = self->velocity.z = self->velocity.y = 0;
25504             set_death(self, attack->attack_type, 0);
25505         }
25506         else
25507         {
25508             if (fall_flag >= 1) return 1;
25509             self->velocity.x = attack->dropv.x;
25510             self->velocity.z = attack->dropv.z;
25511             if(self->direction == DIRECTION_RIGHT)
25512             {
25513                 self->velocity.x = -self->velocity.x;
25514             }
25515             if(self->inbackpain) self->velocity.x *= -1;
25516             toss(self, attack->dropv.y);
25517             self->damage_on_landing.attack_force = attack->damage_on_landing.attack_force;
25518             self->damage_on_landing.attack_type = attack->damage_on_landing.attack_type;
25519             self->knockdowncount = self->modeldata.knockdowncount; // reset the knockdowncount
25520             self->knockdowntime = 0;
25521 
25522             // If no fall/die animations exist, entity simply disappears.
25523             if(!set_fall(self, other, attack, 1))
25524             {
25525                 if(self->modeldata.type & TYPE_PLAYER)
25526                 {
25527                     player_die();
25528                 }
25529                 else
25530                 {
25531                     kill_entity(self);
25532                 }
25533                 return 1;
25534             }
25535         }
25536         if(self->modeldata.type & TYPE_PLAYER)
25537         {
25538             if (savedata.joyrumble[self->playerindex]) control_rumble(self->playerindex, 1, attack->attack_force * 3);
25539         }
25540     }
25541     else if(attack->grab && !attack->no_pain)
25542     {
25543         self->takeaction = common_pain;
25544         other->takeaction = common_grabattack;
25545         other->stalltime = _time + GRAB_STALL;
25546         self->releasetime = _time + (GAME_SPEED / 2);
25547         set_pain(self, self->last_damage_type, 0);
25548     }
25549     // Don't change to pain animation if frozen
25550     else if(!self->frozen && !self->modeldata.nopain && !attack->no_pain && !(self->defense[attack->attack_type].pain && attack->attack_force < self->defense[attack->attack_type].pain))
25551     {
25552         self->takeaction = common_pain;
25553         set_pain(self, self->last_damage_type, 1);
25554     }
25555 
25556     return 1;
25557 }
25558 
common_try_runattack(entity * target)25559 int common_try_runattack(entity *target)
25560 {
25561     if(!self->running || !validanim(self, ANI_RUNATTACK))
25562     {
25563         return 0;
25564     }
25565 
25566 
25567     if(!target)
25568     {
25569         target = normal_find_target(ANI_RUNATTACK, 0);
25570     }
25571 
25572     if(target)
25573     {
25574         if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE))
25575         {
25576             return 0;
25577         }
25578         self->takeaction = common_attack_proc;
25579         self->velocity.z = self->velocity.x = 0;
25580         set_attacking(self);
25581         ent_set_anim(self, ANI_RUNATTACK, 0);
25582         return 1;
25583     }
25584     return 0;
25585 }
25586 
25587 // Active blocking (nopassiveblock enabled).
25588 //
25589 // AI can behave more like players when blocking. Normally AI
25590 // blocking is passive. IOW, it can only choose to block attacks
25591 // as they hit. This function allows the AI to initiate blocking
25592 // preemptively the way players have to.
25593 //
25594 // AI blocks if following conditions are met:
25595 //
25596 // 1. Entity has nopassiveblock enabled.
25597 // 2. Target is within range of BLOCK animation.
25598 // 3. Target is actively attacking.
25599 // 4. Blocking chance passes (same rules as passive blocking).
common_try_block(entity * target)25600 int common_try_block(entity *target)
25601 {
25602 	// Must have block animation.
25603 	if (!validanim(self, ANI_BLOCK))
25604 	{
25605 		return 0;
25606 	}
25607 
25608 	// Exit if we choose not to block. This function includes
25609 	// the check for passive blocking flag.
25610 	if (!check_blocking_decision(self))
25611 	{
25612 		return 0;
25613 	}
25614 
25615 	// If no current target, use block range.
25616     if(!target)
25617     {
25618         target = block_find_target(ANI_BLOCK, 0);
25619     }
25620 
25621 	// Still no target? Nothing to do, so exit.
25622 	if (!target)
25623 	{
25624 		return 0;
25625 	}
25626 
25627     // If target is attacking, let's block and return true.
25628     if(target->attacking != ATTACKING_NONE)
25629     {
25630 		// Set up flags, action, and blocking animations.
25631 		do_active_block(self);
25632 
25633         return 1;
25634     }
25635 
25636     return 0;
25637 }
25638 
25639 // this logic could be used for multiple times, so make a function
25640 // pick a random attack or return the first attacks if testonly is set
25641 // when testonly is set, the function will not check special attacks (upper, jumpattack)
25642 // if target is NULL, ranges are not checked
pick_random_attack(entity * target,int testonly)25643 int pick_random_attack(entity *target, int testonly)
25644 {
25645     int found = 0, i, j;
25646 
25647     for(i = 0; i < max_attacks; i++) // TODO: recheck range for attacks chains
25648     {
25649         if(validanim(self, animattacks[i]) &&
25650                 (!target || check_range_target_all(self, target, animattacks[i])))
25651         {
25652             for(j = ((5 - i) >= 0 ? (5 - i) : 0) * 3; j >= 0; j--)
25653             {
25654                 atkchoices[found++] = animattacks[i];
25655             }
25656         }
25657     }
25658     for(i = 0; i < max_freespecials; i++)
25659     {
25660         if(validanim(self, animspecials[i]) &&
25661                 (check_energy(COST_CHECK_MP, animspecials[i]) ||
25662                  check_energy(COST_CHECK_HP, animspecials[i])) &&
25663                 (!target || check_range_target_all(self, target, animspecials[i])))
25664         {
25665             atkchoices[found++] = animspecials[i];
25666         }
25667     }
25668     if( validanim(self, ANI_THROWATTACK) &&
25669             self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
25670             (!target || check_range_target_all(self, target, ANI_THROWATTACK) ))
25671     {
25672         atkchoices[found++] = ANI_THROWATTACK;
25673     }
25674 
25675     if(testonly)
25676     {
25677         if(found)
25678         {
25679             return atkchoices[(rand32() & 0xffff) % found];
25680         }
25681         return -1;
25682     }
25683 
25684     if( validanim(self, ANI_JUMPATTACK) &&
25685             (!target || check_range_target_all(self, target, ANI_JUMPATTACK)) )
25686     {
25687         if(testonly)
25688         {
25689             return ANI_JUMPATTACK;
25690         }
25691         atkchoices[found++] = ANI_JUMPATTACK;
25692     }
25693     if( validanim(self, ANI_UPPER) &&
25694             (!target || check_range_target_all(self, target, ANI_UPPER)) )
25695     {
25696         if(testonly)
25697         {
25698             return ANI_UPPER;
25699         }
25700         atkchoices[found++] = ANI_UPPER;
25701     }
25702 
25703     if(found)
25704     {
25705         return atkchoices[(rand32() & 0xffff) % found];
25706     }
25707 
25708     return -1;
25709 }
25710 
25711 
25712 // code to lower the chance of attacks, may change while testing old mods
25713 // min - min attack chance
25714 // max - max attack chance
check_attack_chance(entity * target,float min,float max)25715 int check_attack_chance(entity *target, float min, float max)
25716 {
25717 
25718     float chance, chance1, chance2;//, aggfix;
25719 
25720     if(self->modeldata.aiattack & AIATTACK1_ALWAYS)
25721     {
25722         return 1;
25723     }
25724 
25725     chance1 = MIN(1.0f, (diff(self->position.x, self->destx) + diff(self->position.z, self->destz)) / (videomodes.hRes + videomodes.vRes) * move_noatk_factor);
25726     chance2 = MIN(1.0f, (count_ents(self->modeldata.type) - 1) * group_noatk_factor);
25727 
25728     chance = (1.0f - chance1) * (1.0f - chance2);
25729 
25730     if(chance > max)
25731     {
25732         chance = max;
25733     }
25734     else if(chance < min)
25735     {
25736         chance = min;
25737     }
25738 
25739     chance *= (1.0 - self->modeldata.attackthrottle);
25740 
25741     if(self->position.x < screenx - 10 || self->position.x > screenx + videomodes.hRes + 10)
25742     {
25743         if (self->modeldata.offscreen_noatk_factor != 0) chance *= (1.0 - self->modeldata.offscreen_noatk_factor);
25744         else chance *= (1.0 - offscreen_noatk_factor);
25745     }
25746 
25747     return (randf(1) <= chance);
25748 }
25749 
25750 //make a function, mostly for debug purpose
25751 //give it a chance to reset current noattack timer
recheck_nextattack(entity * target)25752 u32 recheck_nextattack(entity *target)
25753 {
25754     if(target->blocking)
25755     {
25756         self->nextattack = 0;
25757     }
25758     else if(target->attacking != ATTACKING_NONE && self->nextattack > 4)
25759     {
25760         self->nextattack -= 4;
25761     }
25762     else if(target->jumping && self->nextattack > 16)
25763     {
25764         self->nextattack -= 16;
25765     }
25766 
25767     return self->nextattack;
25768 }
25769 
common_try_normalattack(entity * target)25770 int common_try_normalattack(entity *target)
25771 {
25772     target = normal_find_target(-1, 0);
25773 
25774     if(!target)
25775     {
25776         return 0;
25777     }
25778 
25779     recheck_nextattack(target);
25780 
25781     if(recheck_nextattack(target) > _time)
25782     {
25783         return 0;
25784     }
25785 
25786     if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE || target->takeaction == common_rise))
25787     {
25788         return 0;
25789     }
25790 
25791     if(pick_random_attack(target, 1) >= 0)
25792     {
25793         if(self->combostep[0] && self->combotime > _time)
25794         {
25795             self->stalltime = _time + 1;
25796         }
25797         else
25798         {
25799             if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance))
25800             {
25801                 self->nextattack = _time + randf(self->modeldata.attackthrottletime);
25802                 return 0;
25803             }
25804             else
25805             {
25806                 self->stalltime = _time + (int)randf((float)MAX(1, GAME_SPEED * 3 / 4 - self->modeldata.aggression));
25807             }
25808         }
25809 
25810         self->takeaction = normal_prepare;
25811         self->velocity.z = self->velocity.x = 0;
25812         set_idle(self);
25813         self->idling = IDLING_NONE; // not really idle, in fact it is thinking
25814         self->attacking = ATTACKING_PREPARED; // pre-attack, for AI-block check
25815         return 1;
25816     }
25817 
25818     return 0;
25819 }
25820 
common_try_jumpattack(entity * target)25821 int common_try_jumpattack(entity *target)
25822 {
25823     entity *dust;
25824     int rnum, ani = 0;
25825     if((validanim(self, ANI_JUMPATTACK) || validanim(self, ANI_JUMPATTACK2)))
25826     {
25827         if(!validanim(self, ANI_JUMPATTACK))
25828         {
25829             rnum = 1;
25830         }
25831         else if(validanim(self, ANI_JUMPATTACK2) && (rand32() & 1))
25832         {
25833             rnum = 1;
25834         }
25835         else
25836         {
25837             rnum = 0;
25838         }
25839 
25840         if(rnum == 0 &&
25841                 // do a jumpattack
25842                 (target || (target = normal_find_target(ANI_JUMPATTACK, 0))) )
25843         {
25844             if(recheck_nextattack(target) > _time)
25845             {
25846                 return 0;
25847             }
25848 
25849             if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE))
25850             {
25851                 rnum = -1;
25852             }
25853             else
25854             {
25855                 if(!check_attack_chance(target, 0.05f, 0.4f))
25856                 {
25857                     self->nextattack = _time + randf(self->modeldata.attackthrottletime);
25858                     return 0;
25859                 }
25860                 //ent_set_anim(self, ANI_JUMPATTACK, 0);
25861                 ani = ANI_JUMPATTACK;
25862                 if(self->direction == DIRECTION_RIGHT)
25863                 {
25864                     self->velocity.x = (float)1.3;
25865                 }
25866                 else
25867                 {
25868                     self->velocity.x = (float) - 1.3;
25869                 }
25870                 self->velocity.z = 0;
25871             }
25872         }
25873         else if(rnum == 1 &&
25874                 // do a jumpattack2
25875                 (target || (target = normal_find_target(ANI_JUMPATTACK2, 0))) )
25876         {
25877             if(recheck_nextattack(target) > _time)
25878             {
25879                 return 0;
25880             }
25881 
25882             if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE))
25883             {
25884                 rnum = -1;
25885             }
25886             else
25887             {
25888                 if(!check_attack_chance(target, 0.05f, 0.5f))
25889                 {
25890                     self->nextattack = _time + randf(self->modeldata.attackthrottletime);
25891                     return 0;
25892                 }
25893                 //ent_set_anim(self, ANI_JUMPATTACK2, 0);
25894                 ani = ANI_JUMPATTACK2;
25895                 self->velocity.x = self->velocity.z = 0;
25896             }
25897         }
25898         else
25899         {
25900             rnum = -1;
25901         }
25902 
25903         if(rnum >= 0)
25904         {
25905 
25906             self->takeaction = common_jump;
25907             set_attacking(self);
25908             self->jumping = 1;
25909             toss(self, self->modeldata.jumpheight);
25910 
25911             if(self->modeldata.dust.jump_start >= 0)
25912             {
25913                 dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_start, NULL);
25914                 if(dust)
25915                 {
25916                     dust->spawntype = SPAWN_TYPE_DUST_JUMP;
25917                     dust->base = self->position.y;
25918                     dust->autokill |= AUTOKILL_ANIMATION_COMPLETE;
25919                     execute_onspawn_script(dust);
25920                 }
25921             }
25922 
25923             ent_set_anim(self, ani, 0);
25924 
25925             return 1;
25926         }
25927     }
25928 
25929     return 0;
25930 }
25931 
common_try_grab(entity * other)25932 int common_try_grab(entity *other)
25933 {
25934     int trygrab(entity * t);
25935 
25936     // old rand()
25937     if( (rand32() & 7) == 0 &&
25938             (validanim(self, ANI_THROW) ||
25939              validanim(self, ANI_GRAB)) && self->idling &&
25940             (other || (other = find_ent_here(self, self->position.x, self->position.z, self->modeldata.hostile, NULL))) &&
25941             trygrab(other))
25942     {
25943         return 1;
25944     }
25945 
25946     return 0;
25947 }
25948 
25949 // A.I. try upper cut
common_try_upper(entity * target)25950 int common_try_upper(entity *target)
25951 {
25952     if(!validanim(self, ANI_UPPER))
25953     {
25954         return 0;
25955     }
25956 
25957     if(!target)
25958     {
25959         target = normal_find_target(ANI_UPPER, 0);
25960     }
25961 
25962     // Target jumping? Try uppercut!
25963     if(target && target->jumping)
25964     {
25965         /*self->takeaction = common_attack_proc;
25966         set_attacking(self);
25967         self->velocity.z = self->velocity.x = 0;
25968         // Don't waste any time!
25969         ent_set_anim(self, ANI_UPPER, 0);*/
25970 
25971         if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance))
25972         {
25973             self->nextattack = _time + randf(self->modeldata.attackthrottletime);
25974             return 0;
25975         }
25976         else
25977         {
25978             self->stalltime = _time + (int)randf((float)MAX(1, GAME_SPEED * 3 / 4 - self->modeldata.aggression));
25979         }
25980 
25981         self->takeaction = upper_prepare;
25982         self->velocity.z = self->velocity.x = 0;
25983         set_idle(self);
25984         self->idling = IDLING_NONE; // not really idle, in fact it is thinking
25985         self->attacking = ATTACKING_PREPARED; // pre-attack, for AI-block check
25986         return 1;
25987     }
25988 
25989     return 0;
25990 }
25991 
common_try_duckattack(entity * other)25992 int common_try_duckattack(entity *other)
25993 {
25994     int them;
25995     entity *target = NULL;
25996 
25997     if(!validanim(self, ANI_DUCKATTACK) || !(self->ducking & DUCK_ACTIVE))
25998     {
25999         return 0;
26000     }
26001 
26002     if(_time / THINK_SPEED % 4 == 0)
26003     {
26004         return 0;
26005     }
26006 
26007     if(self->projectile != BLAST_NONE)
26008     {
26009         them = self->modeldata.projectilehit;
26010     }
26011     else
26012     {
26013         them = self->modeldata.candamage;
26014     }
26015 
26016     if(self->custom_target == NULL || !self->custom_target->exists ) target = normal_find_target(-1, 0);
26017     else target = self->custom_target;
26018 
26019     if(target && !(target->modeldata.type & them))
26020     {
26021         return 0;
26022     }
26023 
26024     if(recheck_nextattack(target) > _time)
26025     {
26026         return 0;
26027     }
26028 
26029     if(!target || !(target->ducking & DUCK_ACTIVE))
26030     {
26031         return 0;
26032     }
26033 
26034     if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance))
26035     {
26036         self->nextattack = _time + randf(self->modeldata.attackthrottletime);
26037         return 0;
26038     }
26039     else
26040     {
26041         self->stalltime = _time + (int)randf((float)MAX(1, GAME_SPEED * 3 / 4 - self->modeldata.aggression));
26042     }
26043 
26044     // finally attack!
26045     self->takeaction = common_attack_proc;
26046     set_attacking(self);
26047     self->velocity.z = self->velocity.x = 0;
26048     // Don't waste any time!
26049     ent_set_anim(self, ANI_DUCKATTACK, 0);
26050 
26051     return 1;
26052 }
26053 
26054 // Normal attack style
26055 // Used by root A.I., what to do if a target is found.
26056 // return 0 if no action is token
26057 // return 1 if an action is token
normal_attack()26058 int normal_attack()
26059 {
26060     //int rnum;
26061 
26062     //rnum = rand32()&7;
26063     if( common_try_duckattack(NULL) ||
26064         common_try_grab(NULL) ||
26065         common_try_upper(NULL) ||
26066         common_try_block(NULL) ||
26067         common_try_runattack(NULL) ||
26068         //(rnum < 2 && common_try_freespecial(NULL)) ||
26069         common_try_normalattack(NULL) ||
26070         common_try_jumpattack(NULL) )
26071     {
26072         self->running = 0;
26073         return 1;
26074     }
26075 
26076     return 0;// nothing to do? so go to next think step
26077 }
26078 
26079 // A.I. characters do a throw
common_throw()26080 void common_throw()
26081 {
26082     if(self->animating)
26083     {
26084         return;    // just play the throw animation
26085     }
26086 
26087     // we have done the throw, return to A.I. root
26088     self->takeaction = NULL;
26089 
26090     set_idle(self);
26091 }
26092 
26093 // toss the grabbed one
dothrow()26094 void dothrow()
26095 {
26096     s_collision_attack attack;
26097     entity *other;
26098     self->velocity.x = self->velocity.z = 0;
26099     other = self->link;
26100     if(other == NULL) //change back to idle, or we will get stuck here
26101     {
26102         self->takeaction = NULL;// A.I. root again
26103         set_idle(self);
26104         return;
26105     }
26106 
26107     if(other->modeldata.throwheight)
26108     {
26109         toss(other, other->modeldata.throwheight);
26110     }
26111     else
26112     {
26113         toss(other, other->modeldata.jumpheight);
26114     }
26115 
26116     other->direction = self->direction;
26117     other->projectile |= BLAST_TOSS;
26118     other->velocity.x = (other->direction == DIRECTION_RIGHT) ? (-other->modeldata.throwdist) : (other->modeldata.throwdist);
26119 
26120     if(autoland == 1 && validanim(other, ANI_LAND))
26121     {
26122         other->damage_on_landing.attack_force = ATTACK_FORCE_LAND_AUTO;
26123     }
26124     else
26125     {
26126         other->damage_on_landing.attack_force = self->modeldata.throwdamage;
26127     }
26128 
26129     ent_unlink(other);
26130 
26131     other->takeaction = common_fall;
26132     self->takeaction = common_throw;
26133 
26134     // Use default attack values.
26135     attack = emptyattack;
26136     set_fall(other, self, &attack, 0);
26137     ent_set_anim(self, ANI_THROW, 0);
26138 }
26139 
26140 
26141 // Waiting until throw frame reached
common_throw_wait()26142 void common_throw_wait()
26143 {
26144     if(!self->link)
26145     {
26146         self->takeaction = NULL;// A.I. root again
26147         set_idle(self);
26148         return;
26149     }
26150 
26151     self->releasetime += THINK_SPEED; //extend release time
26152 
26153     if(self->animpos != self->modeldata.throwframewait)
26154     {
26155         return;
26156     }
26157 
26158     dothrow();
26159 }
26160 
26161 
common_prethrow()26162 void common_prethrow()
26163 {
26164     self->running = 0;    // Quits running if grabbed by opponent
26165 
26166     // Just check if we're still grabbed...
26167     if(self->link)
26168     {
26169         return;
26170     }
26171 
26172     self->takeaction = NULL;// A.I. root again
26173 
26174     set_idle(self);
26175 }
26176 
26177 // warp to its parent entity, just like skeletons in Diablo 2
npc_warp()26178 void npc_warp()
26179 {
26180     if(!self->parent)
26181     {
26182         return;
26183     }
26184     self->position.z = self->parent->position.z;
26185     self->position.x = self->parent->position.x;
26186     self->position.y = self->parent->position.y;
26187     self->velocity.x = self->velocity.z = 0;
26188     self->base = self->parent->base;
26189     self->velocity.y = 0;
26190 
26191     if(validanim(self, ANI_RESPAWN))
26192     {
26193         self->takeaction = common_spawn;
26194         ent_set_anim(self, ANI_RESPAWN, 0);
26195     }
26196     else if(validanim(self, ANI_SPAWN))
26197     {
26198         self->takeaction = common_spawn;
26199         ent_set_anim(self, ANI_SPAWN, 0);
26200     }
26201 }
26202 
adjust_grabposition(entity * ent,entity * other,float dist,int grabin)26203 int adjust_grabposition(entity *ent, entity *other, float dist, int grabin)
26204 {
26205 	float x1;
26206 	float z1;
26207 	float x2;
26208 	float z2;
26209 	float x;
26210 
26211     if(diff(ent->position.y,other->position.y) > T_WALKOFF)
26212     {
26213         return 0;
26214     }
26215 
26216 	if(diff(ent->base,other->base) > T_WALKOFF)
26217     {
26218         return 0;
26219     }
26220 
26221     if(grabin == 1)
26222     {
26223         x1 = ent->position.x;
26224         z1 = z2 = ent->position.z;
26225         x2 = ent->position.x + ((other->position.x > ent->position.x) ? dist : -dist);
26226     }
26227     else
26228     {
26229         x = (ent->position.x + other->position.x) / 2;
26230         x1 = x + ((ent->position.x >= other->position.x) ? (dist / 2) : (-dist / 2));
26231         x2 = x + ((other->position.x > ent->position.x) ? (dist / 2) : (-dist / 2));
26232         z1 = z2 = (ent->position.z + other->position.z) / 2;
26233     }
26234 
26235     if(0 >= testmove(ent, ent->position.x, ent->position.z, x1, z1) || 0 >= testmove(other, other->position.x, other->position.z, x2, z2))
26236     {
26237         return 0;
26238     }
26239 
26240     ent->position.x = x1;
26241     ent->position.z = z1;
26242     other->position.x = x2;
26243     other->position.z = z2;
26244     //other->position.y = ent->position.y;
26245     //other->base = ent->base;
26246 
26247 	// Sort order control.
26248 	//
26249 	// If grabback is set (grabback = 1) the grabbed entity's
26250 	// sort order is forced 1 greater than grappler so grappler
26251 	// appears behind. Otherwise grabbed is moved one lower,
26252 	// forcing grappler to appear in front.
26253 	if (ent->modeldata.grabback)
26254 	{
26255 		other->sortid = ent->sortid + 1;
26256 	}
26257 	else if (!ent->modeldata.grabback)
26258 	{
26259 		other->sortid = ent->sortid - 1;
26260 	}
26261 
26262 
26263     return 1;
26264 }
26265 
dograb(entity * attacker,entity * target,e_dograb_adjustcheck adjustcheck)26266 int dograb(entity *attacker, entity *target, e_dograb_adjustcheck adjustcheck)
26267 {
26268     /*
26269     Execute grab action. Added by splitting off trygrab
26270     so entities can be forced to perform grab by script.
26271     Damon V. Caskey
26272     2013-12-30
26273 
26274     Attacker: Entity performing grab.
26275     target, entity being grabbed.
26276     */
26277 
26278     int result  = 0; //Output value.
26279     int pass    = 1; //Adjust pass/fail.
26280 
26281     /* If an adjust check is needed, make sure adjusting did not fail. */
26282     if(adjustcheck == DOGRAB_ADJUSTCHECK_TRUE)
26283     {
26284         pass = adjust_grabposition(attacker, target, attacker->modeldata.grabdistance, 0);
26285     }
26286 
26287     /* If adjust_grabposition passed (or wasn't needed) perform grab actions. */
26288     if(pass)
26289     {
26290         if(attacker->model->grabflip & 1)
26291         {
26292             attacker->direction = (attacker->position.x < target->position.x);
26293         }
26294 
26295         /* Set flags. */
26296         set_opponent(target, attacker);
26297         ents_link(attacker, target);
26298         target->attacking = ATTACKING_NONE;
26299         attacker->idling = IDLING_NONE;
26300         attacker->running = 0;
26301         attacker->ducking = DUCK_NONE;
26302         attacker->inbackpain = 0;
26303 
26304         /* Stop all movement. */
26305         attacker->velocity.x = 0;
26306         attacker->velocity.z = 0;
26307         target->velocity.x = 0;
26308         target->velocity.z = 0;
26309 
26310         /* Check for grab animation, otherwise use orginal throwing system. */
26311         if(validanim(self, ANI_GRAB))
26312         {
26313             if(attacker->model->grabflip & 2)
26314             {
26315                 target->direction = !attacker->direction;
26316             }
26317             attacker->attacking = ATTACKING_NONE;
26318             memset(attacker->combostep, 0, 5 * sizeof(*attacker->combostep));
26319             target->stalltime = _time + GRAB_STALL;
26320             attacker->releasetime = _time + (GAME_SPEED / 2);
26321             target->takeaction = common_grabbed;
26322             attacker->takeaction = common_grab;
26323             ent_set_anim(attacker, ANI_GRAB, 0);
26324             set_pain(target, -1, 0); //set grabbed animation
26325         }
26326         else
26327         {
26328             /*
26329             If no throwframewait use original throw code immediately.
26330             Otherwise use throwframewait.
26331             */
26332             if(attacker->modeldata.throwframewait == -1)
26333             {
26334                 dothrow();
26335             }
26336             else
26337             {
26338                 if(self->model->grabflip & 2)
26339                 {
26340                     target->direction = !attacker->direction;
26341                 }
26342 
26343                 target->takeaction = common_prethrow;
26344                 attacker->takeaction = common_throw_wait;
26345                 ent_set_anim(attacker, ANI_THROW, 0);
26346                 set_pain(target, -1, 0); // set grabbed animation
26347             }
26348         }
26349 
26350         result = 1;
26351     }
26352 
26353     return result;
26354 }
26355 
trygrab(entity * other)26356 int trygrab(entity *other)
26357 {
26358     /*
26359     Actions after grab possibility check moved to dograb.
26360     Damon V. Caskey
26361     2013-12-30
26362 
26363     other: Grab target.
26364     */
26365 
26366     int result = 0; //return value.
26367 
26368     if(cangrab(self, other))
26369     {
26370         result = dograb(self, other, DOGRAB_ADJUSTCHECK_TRUE);
26371     }
26372 
26373     return result;
26374 }
26375 
check_entity_collision(entity * ent,entity * target)26376 int check_entity_collision(entity *ent, entity *target)
26377 {
26378     s_hitbox *coords_col_entity_ent;
26379     s_hitbox *coords_col_entity_target;
26380     s_collision_entity  *col_entity_ent = NULL;
26381     s_collision_entity  *col_entity_target = NULL;
26382     int     x1,
26383             x2,
26384             y1,
26385             y2,
26386             z1,
26387             z2;
26388     int col_entity_ent_instance;
26389     int     col_entity_ent_pos_x        = 0,
26390             col_entity_ent_pos_y        = 0,
26391             col_entity_ent_size_x       = 0,
26392             col_entity_ent_size_y       = 0,
26393             col_entity_target_pos_x     = 0,
26394             col_entity_target_pos_y     = 0,
26395             col_entity_target_size_x    = 0,
26396             col_entity_target_size_y    = 0;
26397     int     zdist = 0;
26398     int     zdepth1 = 0, zdepth2 = 0;
26399     int     entity_pushing = ent->modeldata.entitypushing;
26400     float   PUSH_FACTOR = ent->modeldata.pushingfactor;
26401 
26402     if(ent == target
26403        || !target->animation->collision_entity
26404        || !ent->animation->collision_entity
26405        )
26406     {
26407         return 0;
26408     }
26409 
26410     if(ent->link || target->link)
26411     {
26412         return 0;
26413     }
26414 
26415     int col_entity_target_instance = 0;
26416     int collision_found = 0;
26417 
26418     if (entity_pushing && !PUSH_FACTOR) PUSH_FACTOR = 1.0f;
26419 
26420     for(col_entity_ent_instance = 0; col_entity_ent_instance < max_collisons; col_entity_ent_instance++)
26421     {
26422         col_entity_ent  = ent->animation->collision_entity[ent->animpos]->instance[col_entity_ent_instance];
26423         coords_col_entity_ent   = col_entity_ent->coords;
26424 
26425         for(col_entity_target_instance = 0; col_entity_target_instance < max_collisons; col_entity_target_instance++)
26426         {
26427             col_entity_target          = target->animation->collision_entity[target->animpos]->instance[col_entity_target_instance];
26428             coords_col_entity_target   = col_entity_target->coords;
26429 
26430             z1      = ent->position.z + ent->movez;
26431             z2      = target->position.z + target->movez;
26432             zdist   = 0;
26433 
26434             if(coords_col_entity_ent->z2 > coords_col_entity_ent->z1)
26435             {
26436                 zdepth1 = (coords_col_entity_ent->z2 - coords_col_entity_ent->z1) / 2;
26437                 z1 += coords_col_entity_ent->z1 + zdepth1;
26438                 zdist += zdepth1;
26439             }
26440             else if(coords_col_entity_ent->z1)
26441             {
26442                 zdepth1 = coords_col_entity_ent->z1;
26443                 zdist += coords_col_entity_ent->z1;
26444             }
26445 
26446             if(coords_col_entity_target->z2 > coords_col_entity_target->z1)
26447             {
26448                 zdepth2 = (coords_col_entity_target->z2 - coords_col_entity_target->z1) / 2;
26449                 z2 += coords_col_entity_target->z1 + zdepth2;
26450                 zdist += zdepth2;
26451             }
26452             else if(coords_col_entity_target->z1)
26453             {
26454                 zdepth2 = coords_col_entity_target->z1;
26455                 zdist += coords_col_entity_target->z1;
26456             }
26457 
26458             if(diff(z1, z2) > zdist)
26459             {
26460                 continue;
26461             }
26462 
26463             x1 = (int)ent->position.x + ent->movex;
26464             z1 = (int)ent->position.z + ent->movez;
26465             y1 = (int)z1 - ent->position.y;
26466             x2 = (int)target->position.x + target->movex;
26467             z2 = (int)target->position.z + target->movez;
26468             y2 = (int)z2 - target->position.y;
26469 
26470             if(ent->direction == DIRECTION_LEFT)
26471             {
26472                 col_entity_ent_pos_x   = x1 - coords_col_entity_ent->width;
26473                 col_entity_ent_size_x  = x1 - coords_col_entity_ent->x;
26474             }
26475             else
26476             {
26477                 col_entity_ent_pos_x    = x1 + coords_col_entity_ent->x;
26478                 col_entity_ent_size_x   = x1 + coords_col_entity_ent->width;
26479             }
26480             col_entity_ent_pos_y    = y1 + coords_col_entity_ent->y;
26481             col_entity_ent_size_y   = y1 + coords_col_entity_ent->height;
26482 
26483             if(target->direction == DIRECTION_LEFT)
26484             {
26485                 col_entity_target_pos_x    = x2 - coords_col_entity_target->width;
26486                 col_entity_target_size_x   = x2 - coords_col_entity_target->x;
26487             }
26488             else
26489             {
26490                 col_entity_target_pos_x    = x2 + coords_col_entity_target->x;
26491                 col_entity_target_size_x   = x2 + coords_col_entity_target->width;
26492             }
26493             col_entity_target_pos_y    = y2 + coords_col_entity_target->y;
26494             col_entity_target_size_y   = y2 + coords_col_entity_target->height;
26495 
26496             if(col_entity_ent_pos_x > col_entity_target_size_x)
26497             {
26498                 continue;
26499             }
26500             if(col_entity_target_pos_x > col_entity_ent_size_x)
26501             {
26502                 continue;
26503             }
26504             if(col_entity_ent_pos_y > col_entity_target_size_y)
26505             {
26506                 continue;
26507             }
26508             if(col_entity_target_pos_y > col_entity_ent_size_y)
26509             {
26510                 continue;
26511             }
26512 
26513             // If we got this far, set collision flag
26514             // and break this loop.
26515             collision_found = 1;
26516             break;
26517         }
26518 
26519         // If a collision was found
26520         // break out of loop.
26521         if(collision_found)
26522         {
26523             break;
26524         }
26525     }
26526 
26527     if(!collision_found)
26528     {
26529         return 0;
26530     }
26531 
26532     // check on axis x
26533     if(col_entity_ent_pos_x <= col_entity_target_pos_x)
26534     {
26535         if (!entity_pushing)
26536         {
26537             if (ent->movex > 0) ent->movex = 0;
26538         }
26539         else
26540         {
26541             // let to escape
26542             if (ent->collided_entity && ent->collided_entity == target)
26543             {
26544                 if (ent->movex > 0) ent->movex = 0;
26545             }
26546             if (ent->movex != -1 * target->movex || (!target->movex)) ent->movex -= PUSH_FACTOR;
26547         }
26548     }
26549     else
26550     {
26551         if (!entity_pushing)
26552         {
26553             if (ent->movex < 0) ent->movex = 0;
26554         }
26555         else
26556         {
26557             // let to escape
26558             if (ent->collided_entity && ent->collided_entity == target)
26559             {
26560                 if (ent->movex < 0) ent->movex = 0;
26561             }
26562             if (ent->movex != -1 * target->movex || (!target->movex)) ent->movex += PUSH_FACTOR;
26563         }
26564     }
26565 
26566     // check on axis z
26567     if(z1 - zdepth1 <= z2 + zdepth2 &&
26568        z1 - zdepth1 >= z2)
26569     {
26570         if (!entity_pushing)
26571         {
26572             if (ent->movez < 0) ent->movez = 0;
26573         }
26574         else
26575         {
26576             // let to escape
26577             if (ent->collided_entity && ent->collided_entity == target)
26578             {
26579                 if (ent->movez < 0) ent->movez = 0;
26580             }
26581             if (ent->movez != -1 * target->movez || (!target->movez)) ent->movez += PUSH_FACTOR;
26582         }
26583     }
26584     else if(z1 + zdepth1 >= z2 - zdepth2 &&
26585             z1 + zdepth1 <= z2)
26586     {
26587         if (!entity_pushing)
26588         {
26589             if (ent->movez > 0) ent->movez = 0;
26590         }
26591         else
26592         {
26593             // let to escape
26594             if (ent->collided_entity && ent->collided_entity == target)
26595             {
26596                 if (ent->movez > 0) ent->movez = 0;
26597             }
26598             if (ent->movez != -1 * target->movez || (!target->movez)) ent->movez -= PUSH_FACTOR;
26599         }
26600     }
26601 
26602     // execute event
26603     execute_onentitycollision_script(ent, target, col_entity_ent, col_entity_target);
26604 
26605     return 1;
26606 }
26607 
check_entity_collision_for(entity * ent)26608 void check_entity_collision_for(entity* ent)
26609 {
26610     // Animation has collision?
26611     if (ent && ent->animation && ent->animation->collision_entity)
26612     {
26613         int i;
26614         for(i = 0; i < ent_max; i++)
26615         {
26616             //s_anim *a = ent->animation[ent->animnum];
26617             entity* target = ent_list[i];
26618             if(target->exists && target != ent)// && !target->dead && (target->modeldata.type & TYPE_ENEMY)
26619             {
26620                 if (check_entity_collision(ent, target))
26621                 {
26622                     ent->collided_entity = target;
26623                     target->collided_entity = ent;
26624                     return;
26625                 }
26626             }
26627         }
26628     }
26629 
26630     ent->collided_entity = NULL;
26631     return;
26632 }
26633 
common_trymove(float xdir,float zdir)26634 int common_trymove(float xdir, float zdir)
26635 {
26636     entity *other = NULL, *te = NULL;
26637     int wall, heightvar, t, needcheckhole = 0;
26638     float x, z, oxdir, ozdir;
26639 
26640     if(!xdir && !zdir)
26641     {
26642         return 0;
26643     }
26644 
26645     // UT: move player grab logic here to bypass some extra checks
26646     // it used to be at the very end of this function
26647     //------------------ grab/throw checking ------------------
26648 
26649     //printf("rand(): %d rand32(): %d\n",rand(),rand32());
26650     // old rand()
26651     if((self->modeldata.type & TYPE_PLAYER) &&
26652             (rand32() & 7) == 0 &&
26653             (validanim(self, ANI_THROW) ||
26654              validanim(self, ANI_GRAB)) && self->idling &&
26655             (other = find_ent_here(self, self->position.x, self->position.z, self->modeldata.hostile, NULL)))
26656     {
26657         if(trygrab(other))
26658         {
26659             return 0;
26660         }
26661     }
26662     // ---------------  end of grab/throw checking ------------------------
26663 
26664 
26665     oxdir = xdir;
26666     ozdir = zdir;
26667     /*
26668     // entity is grabbed by other
26669     if(self->link && self->link->grabbing==self && self->link->grabwalking)
26670     {
26671     	return 1; // just return so we don't have to check twice
26672     }*/
26673 
26674     x = self->position.x + xdir;
26675     z = self->position.z + zdir;
26676     // -----------bounds checking---------------
26677     // Subjec to Z and out of bounds? Return to level!
26678     if (self->modeldata.subject_to_minz > 0)
26679     {
26680         if(z < PLAYER_MIN_Z)
26681         {
26682             zdir = PLAYER_MIN_Z - self->position.z;
26683             execute_onblockz_script(self);
26684         }
26685     }
26686 
26687     if (self->modeldata.subject_to_maxz > 0)
26688     {
26689         if(z > PLAYER_MAX_Z)
26690         {
26691             zdir = PLAYER_MAX_Z - self->position.z;
26692             execute_onblockz_script(self);
26693         }
26694     }
26695 
26696     // screen checking
26697     if(self->modeldata.subject_to_screen > 0)
26698     {
26699         if(x < advancex + 10)
26700         {
26701             xdir = advancex + 10 - self->position.x;
26702             execute_onblocks_script(self);  //Screen block event.
26703         }
26704         else if(x > advancex + (videomodes.hRes - 10))
26705         {
26706             xdir = advancex + (videomodes.hRes - 10) - self->position.x;
26707             execute_onblocks_script(self);  //Screen block event.
26708         }
26709     }
26710 
26711     if(!xdir && !zdir)
26712     {
26713         return 0;
26714     }
26715     x = self->position.x + xdir;
26716     z = self->position.z + zdir;
26717 
26718     //-----------end of bounds checking-----------
26719 
26720     //-------------hole checking ---------------------
26721     // Don't walk into a hole or walk off platforms into holes
26722     if( self->modeldata.subject_to_hole > 0 && !inair(self) && !(self->modeldata.type & TYPE_PLAYER) && self->idling &&
26723             (!self->animation->move[self->animpos]->base || self->animation->move[self->animpos]->base < 0) &&
26724             !(self->modeldata.aimove & AIMOVE2_IGNOREHOLES))
26725     {
26726 
26727         needcheckhole = 1;
26728         if(zdir && checkhole(self->position.x, z) && checkwall_index(self->position.x, z) < 0 && !check_platform_below(self->position.x, z, self->position.y, self))
26729         {
26730             //int holeind = checkholeindex_in(self->position.x, z, self->position.y);
26731 
26732             zdir = 0;
26733             //execute_inhole_script(self, 2, (double)level->holes[holeind].height, holeind);
26734         }
26735         if(xdir && checkhole(x, self->position.z) && checkwall_index(x, self->position.z) < 0 && !check_platform_below(x, self->position.z, self->position.y, self))
26736         {
26737             //int holeind = checkholeindex_in(x, self->position.z, self->position.y);
26738 
26739             xdir = 0;
26740             //execute_inhole_script(self, 1, (double)level->holes[holeind].height, holeind);
26741         }
26742     }
26743 
26744     if(!xdir && !zdir)
26745     {
26746         return 0;
26747     }
26748     x = self->position.x + xdir;
26749     z = self->position.z + zdir;
26750     //-----------end of hole checking---------------
26751 
26752     //--------------obstacle checking ------------------
26753     if(self->modeldata.subject_to_obstacle > 0 /*&& !inair(self)*/)
26754     {
26755         int hit = 0;
26756 
26757         //TODO, check once instead of twice
26758         if((other = find_ent_here(self, x, self->position.z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) &&
26759                 (xdir > 0 ? other->position.x >= self->position.x : other->position.x <= self->position.x) &&
26760                 (!other->animation->platform || !other->animation->platform[other->animpos][PLATFORM_HEIGHT]))
26761         {
26762             xdir    = 0;
26763             if ( self->falling ) hit |= 1;
26764             te = other;
26765             execute_onblocko_script(self, PLANE_X, other);
26766         }
26767         if((other = find_ent_here(self, self->position.x, z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) &&
26768                 (zdir > 0 ? other->position.z >= self->position.z : other->position.z <= self->position.z) &&
26769                 (!other->animation->platform || !other->animation->platform[other->animpos][PLATFORM_HEIGHT]))
26770         {
26771             zdir    = 0;
26772             if ( self->falling ) hit |= 1;
26773             if(te != other) //just in case they are the same obstacle
26774             {
26775                 execute_onblocko_script(self, PLANE_Z, other);
26776             }
26777         }
26778 
26779         if ( hit && !self->hitwall && validanim(self, ANI_HITOBSTACLE) ) ent_set_anim(self, ANI_HITOBSTACLE, 0);
26780         if ( hit && !self->hitwall ) self->hitwall = 1;
26781     }
26782 
26783     if(!xdir && !zdir)
26784     {
26785         return 0;
26786     }
26787     x = self->position.x + xdir;
26788     z = self->position.z + zdir;
26789 
26790     //-----------end of obstacle checking--------------
26791 
26792     // ---------------- platform checking----------------
26793 
26794     if(self->animation->size.y)
26795     {
26796         heightvar = self->animation->size.y;
26797     }
26798     else
26799     {
26800         heightvar = self->modeldata.size.y;
26801     }
26802 
26803     // Check for obstacles with platform code and adjust base accordingly
26804     if(self->modeldata.subject_to_platform > 0 )
26805     {
26806         int hit = 0;
26807 
26808         //if(xdir>0 ? other->position.x>self->position.x : other->position.x<self->position.x) {xdir = 0; }
26809         //if(zdir>0 ? other->position.z>self->position.z : other->position.z<self->position.z) {zdir = 0; }
26810         //temporary fix for thin platforms (i.e, offset is not between left and right side)
26811         // TODO: find the collision position, merge with wall code
26812         if(xdir && (other = check_platform_between(x, self->position.z, self->position.y, self->position.y + heightvar, self))  )
26813         {
26814             xdir = 0;
26815             if ( self->falling ) hit |= 1;
26816             execute_onblockp_script(self, PLANE_X, other);
26817         }
26818         if(zdir && (other = check_platform_between(self->position.x, z, self->position.y, self->position.y + heightvar, self))  )
26819         {
26820             zdir = 0;
26821             if ( self->falling ) hit |= 1;
26822             execute_onblockp_script(self, PLANE_Z, other);
26823         }
26824 
26825         if ( hit && !self->hitwall && validanim(self, ANI_HITPLATFORM) ) ent_set_anim(self, ANI_HITPLATFORM, 0);
26826         if ( hit && !self->hitwall ) self->hitwall = 1;
26827     }
26828 
26829     if(!xdir && !zdir)
26830     {
26831         return 0;
26832     }
26833     x = self->position.x + xdir;
26834     z = self->position.z + zdir;
26835 
26836     //-----------end of platform checking------------------
26837 
26838     // ------------------ wall checking ---------------------
26839     if(self->modeldata.subject_to_wall > 0)
26840     {
26841         int hit = 0;
26842 
26843         if(xdir && (wall = checkwall_below(x, self->position.z, T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[wall].height > self->position.y)
26844         {
26845             xdir = 0;
26846             if ( self->falling && (self->modeldata.hitwalltype < 0 || (self->modeldata.hitwalltype >= 0 && level->walls[wall].type == self->modeldata.hitwalltype)) ) hit |= 1;
26847             execute_onblockw_script(self, &level->walls[wall], PLANE_X, wall);
26848         }
26849         if(zdir && (wall = checkwall_below(self->position.x, z, T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[wall].height > self->position.y)
26850         {
26851             zdir = 0;
26852             if ( self->falling && (self->modeldata.hitwalltype < 0 || (self->modeldata.hitwalltype >= 0 && level->walls[wall].type == self->modeldata.hitwalltype)) ) hit |= 1;
26853             execute_onblockw_script(self, &level->walls[wall], PLANE_Z, wall);
26854         }
26855 
26856         if ( hit && !self->hitwall && validanim(self, ANI_HITWALL) ) ent_set_anim(self, ANI_HITWALL, 0);
26857         if ( hit && !self->hitwall ) self->hitwall = 1;
26858     }
26859 
26860     if(!xdir && !zdir)
26861     {
26862         return 0;
26863     }
26864     x = self->position.x + xdir;
26865     z = self->position.z + zdir;
26866     //----------------end of wall checking--------------
26867 
26868     /*
26869     // Final check to ensure we don't move into other obstacles.
26870     // The old logic allows xdir if zdir is block and vice versa,
26871     // but has a risk that the new destination is actually blocked
26872     // because of multiple obstacle types.
26873     //
26874     // block  old
26875     //    |  /
26876     //    | /
26877     //    |/__ new
26878     */
26879     //xdir = zdir = 0;
26880     // TODO: should we add some checks in testmove to execute those onblockwhatever scripts?
26881     t = testmove(self, self->position.x, self->position.z, x, z);
26882     // extra hole check, only avoid hole while idling
26883     if(t <= 0 && (t != -2 || needcheckhole))
26884     {
26885         return 0;
26886     }
26887 
26888     // do move and return
26889     self->position.x += xdir;
26890     self->position.z += zdir;
26891 
26892     if(xdir)
26893     {
26894         execute_onmovex_script(self);    //X move event.
26895     }
26896     if(zdir)
26897     {
26898         execute_onmovez_script(self);    //Z move event.
26899     }
26900     return 2 - (xdir == oxdir && zdir == ozdir); // return 2 for some checks
26901 }
26902 
26903 // enemies run off after attack
common_runoff()26904 void common_runoff()
26905 {
26906     entity *target = normal_find_target(-1, 0);
26907 
26908     if(target == NULL)   //sealth checking
26909     {
26910         self->velocity.z = self->velocity.x = 0;
26911         self->takeaction = NULL; // OK, back to A.I. root
26912         set_idle(self);
26913         return;
26914     }
26915 
26916     if(!self->modeldata.noflip)
26917     {
26918         self->direction = (self->position.x < target->position.x);
26919     }
26920     if(self->direction == DIRECTION_RIGHT)
26921     {
26922         self->velocity.x = -self->modeldata.speed / 2;
26923     }
26924     else
26925     {
26926         self->velocity.x = self->modeldata.speed / 2;
26927     }
26928 
26929     self->velocity.z = 0;
26930 
26931     if(_time > self->stalltime)
26932     {
26933         self->takeaction = NULL;    // OK, back to A.I. root
26934     }
26935 
26936     adjust_walk_animation(target);
26937 }
26938 
26939 
26940 /*void common_stuck_underneath()
26941 {
26942     int heightvar = self->animation->size.y ? self->animation->size.y : self->modeldata.size.y;
26943     if(!check_platform_between(self->position.x, self->position.z, self->position.y, self->position.y + heightvar, self) )
26944     {
26945         self->takeaction = NULL;
26946         set_idle(self);
26947         return;
26948     }
26949     if(player[self->playerindex].keys & FLAG_MOVELEFT)
26950     {
26951         self->direction = DIRECTION_LEFT;
26952     }
26953     else if(player[self->playerindex].keys & FLAG_MOVERIGHT)
26954     {
26955         self->direction = DIRECTION_RIGHT;
26956     }
26957     if(player[self->playerindex].playkeys & FLAG_ATTACK && validanim(self, ANI_DUCKATTACK))
26958     {
26959         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
26960         self->takeaction = common_attack_proc;
26961         set_attacking(self);
26962         self->velocity.x = self->velocity.z = 0;
26963         self->combostep[0] = 0;
26964         self->running = 0;
26965         ent_set_anim(self, ANI_DUCKATTACK, 0);
26966         return;
26967     }
26968     if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (player[self->playerindex].playkeys & FLAG_JUMP) && validanim(self, ANI_SLIDE))
26969     {
26970         player[self->playerindex].playkeys &= ~FLAG_JUMP;
26971         self->takeaction = common_attack_proc;
26972         set_attacking(self);
26973         self->velocity.x = self->velocity.z = 0;
26974         self->combostep[0] = 0;
26975         self->running = 0;
26976         ent_set_anim(self, ANI_SLIDE, 0);
26977         return;
26978     }
26979 }*/
26980 
26981 
26982 // finish attacking, do something
common_attack_finish()26983 void common_attack_finish()
26984 {
26985     entity *target;
26986     int stall;
26987 
26988     self->velocity.x = self->velocity.z = 0;
26989 
26990     if(self->modeldata.type & TYPE_PLAYER)
26991     {
26992         if (self->ducking & DUCK_ACTIVE)
26993         {
26994             doduck(self);
26995         }
26996         else
26997         {
26998             self->takeaction = NULL;
26999             set_idle(self);
27000         }
27001         return;
27002     }
27003 
27004     target = self->opponent;
27005 
27006     if(target && !self->modeldata.nomove && self->ducking == DUCK_NONE && diff(self->position.x, target->position.x) < 80 && (rand32() & 3))
27007     {
27008         self->takeaction = NULL;//common_runoff;
27009         self->destx = self->position.x > target->position.x ? MIN(self->position.x + 40, target->position.x + 80) : MAX(self->position.x - 40, target->position.x - 80);
27010         self->destz = self->position.z;
27011         self->velocity.x = self->position.x > target->position.x ? self->modeldata.speed : -self->modeldata.speed;
27012         self->velocity.z = 0;
27013         adjust_walk_animation(target);
27014         self->idling = IDLING_PREPARED;
27015     }
27016     else
27017     {
27018         if (self->ducking & DUCK_ACTIVE)
27019         {
27020             doduck(self);
27021         }
27022         else
27023         {
27024             self->takeaction = NULL;
27025             set_idle(self);
27026         }
27027     }
27028 
27029     stall = GAME_SPEED - self->modeldata.aggression;
27030     if (stall < GAME_SPEED / 2)
27031     {
27032         stall = GAME_SPEED / 2;
27033     }
27034     self->stalltime = _time + MAX(0, stall);
27035 }
27036 
27037 //while playing any simple animation
common_animation_normal()27038 void common_animation_normal()
27039 {
27040     if(self->animating)
27041     {
27042         return;
27043     }
27044 
27045     self->takeaction = NULL;
27046     set_idle(self);
27047 
27048     return;
27049 }
27050 
27051 //while playing attack animation
common_attack_proc()27052 void common_attack_proc()
27053 {
27054     if(self->animating || diff(self->position.y, self->base) >= 4)
27055     {
27056         return;
27057     }
27058 
27059     if(self->tocost)
27060     {
27061         if(self->animation->energycost)
27062         {
27063             // Enemy was hit with a special so go ahead and subtract life
27064             if(check_energy(COST_CHECK_MP, self->animnum))
27065             {
27066                 self->energy_state.mp_current -= self->animation->energycost->cost;
27067             }
27068             else
27069             {
27070                 self->energy_state.health_current -= self->animation->energycost->cost;
27071             }
27072         }
27073         self->tocost = 0;    // Life is subtracted, so go ahead and reset the flag
27074     }
27075 
27076     if(self == smartbomber)
27077     {
27078         // Player is done with the special animation, so unfreeze and execute a smart bomb
27079         smart_bomb(self, self->modeldata.smartbomb);
27080         smartbomber = NULL;
27081     }
27082     if(self->deduct_ammo == 1)
27083     {
27084         subtract_shot();
27085         self->deduct_ammo = 0;
27086     }
27087     self->attacking = ATTACKING_NONE;
27088     // end of attack proc
27089     common_attack_finish();
27090 }
27091 
27092 
27093 // dispatch A.I. attack
common_attack()27094 int common_attack()
27095 {
27096     int aiattack;
27097 
27098     //if(stalker==self) return 0;
27099 
27100     if(_time / THINK_SPEED % 4 == 0)
27101     {
27102         return 0;
27103     }
27104 
27105     if(self->modeldata.aiattack == -1)
27106     {
27107         return 0;
27108     }
27109 
27110     aiattack = self->modeldata.aiattack & MASK_AIATTACK1;
27111 
27112     switch(aiattack)
27113     {
27114     case AIATTACK1_LONG:
27115     case AIATTACK1_MELEE:
27116     case AIATTACK1_NOATTACK:
27117         return 0;
27118     default:                    // this is the only available attack style by now
27119         return inair(self) ? 0 : normal_attack();
27120     }
27121 }
27122 
27123 //maybe used many times, so make a function
27124 // A.I. characters will check if there's a wall infront, and jump onto it if possible
27125 // return 1 if jump
common_try_jump()27126 int common_try_jump()
27127 {
27128     float xdir, zdir;
27129     int wall, j = 0;
27130     float rmin, rmax;
27131 
27132     if(validanim(self, ANI_JUMP)) //Can jump?
27133     {
27134         //Check to see if there is a wall within jumping distance and within a jumping height
27135         xdir = 0;
27136         wall = -1;
27137         rmin = (float)self->modeldata.animation[ANI_JUMP]->range.x.min;
27138         rmax = (float)self->modeldata.animation[ANI_JUMP]->range.x.max;
27139         if(self->direction == DIRECTION_RIGHT)
27140         {
27141             xdir = self->position.x + rmin;
27142         }
27143         else
27144         {
27145             xdir = self->position.x - rmin;
27146         }
27147         //check z jump
27148         if(self->modeldata.jumpmovez)
27149         {
27150             zdir = self->position.z + self->velocity.z;
27151         }
27152         else
27153         {
27154             zdir = self->position.z;
27155         }
27156 
27157         if( (wall = checkwall_below(xdir, zdir, T_MAX_CHECK_ALTITUDE)) >= 0 &&
27158                 level->walls[wall].height <= self->position.y + rmax &&
27159                 !inair(self) && self->position.y < level->walls[wall].height  )
27160         {
27161             j = 1;
27162         }
27163         else if(checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) &&
27164                 checkwall_index(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) < 0 &&
27165                 check_platform (self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir, self) == NULL &&
27166                 !checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? rmax : -rmax), zdir))
27167         {
27168             j = 1;
27169         }
27170     }
27171 
27172     /*
27173     Damon V. Caskey
27174     03292010
27175     AI can will check its RUNJUMP range if JUMP can't reach. Code is pretty redundant,
27176     can probably be moved to a function later.
27177     */
27178     if(!j && validanim(self, ANI_RUNJUMP))														//Jump check failed and can run jump?
27179     {
27180         //Check for wall in range of RUNJUMP.
27181         xdir = 0;
27182         wall = -1;
27183         rmin = (float)self->modeldata.animation[ANI_RUNJUMP]->range.x.min;
27184         rmax = (float)self->modeldata.animation[ANI_RUNJUMP]->range.x.max;
27185         if(self->direction == DIRECTION_RIGHT)
27186         {
27187             xdir = self->position.x + rmin;
27188         }
27189         else
27190         {
27191             xdir = self->position.x - rmin;
27192         }
27193         //check z jump
27194         if(self->modeldata.jumpmovez)
27195         {
27196             zdir = self->position.z + self->velocity.z;
27197         }
27198         else
27199         {
27200             zdir = self->position.z;
27201         }
27202 
27203         if( (wall = checkwall_below(xdir, zdir, T_MAX_CHECK_ALTITUDE)) >= 0 &&
27204                 level->walls[wall].height <= self->position.y + rmax &&
27205                 !inair(self) && self->position.y < level->walls[wall].height  )
27206         {
27207             j = 2;																				//Set to perform runjump.
27208         }
27209         //Check for pit in range of RUNJUMP.
27210         else if(checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) &&
27211                 checkwall_index(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) < 0 &&
27212                 check_platform (self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir, self) == NULL &&
27213                 !checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? rmax : -rmax), zdir))
27214         {
27215             j = 2;																				//Set to perform runjump.
27216         }
27217     }
27218 
27219     if(j)
27220     {
27221         if(self->running || j == 2)
27222         {
27223             if(validanim(self, ANI_RUNJUMP))														//Running or only within range of RUNJUMP?
27224             {
27225                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_RUNJUMP);
27226             }
27227             else if(validanim(self, ANI_FORWARDJUMP))
27228             {
27229                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
27230             }
27231             else
27232             {
27233                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
27234             }
27235         }
27236         else
27237         {
27238             if(validanim(self, ANI_FORWARDJUMP))
27239             {
27240                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
27241             }
27242             else
27243             {
27244                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
27245             }
27246         }
27247 
27248         return 1;
27249     }
27250     return 0;
27251 }
27252 
27253 //test if direction is available for anim_up
testup(float xd,float zd)27254 static int testup(float xd, float zd)
27255 {
27256     float f;
27257     if(zd < 0)
27258     {
27259         if(!xd)
27260         {
27261             return 1;
27262         }
27263         f = zd / xd;
27264         return (f > 0.5 || f < -0.5);
27265     }
27266     return 0;
27267 }
27268 
27269 //test if direction is available for anim_down
testdown(float xd,float zd)27270 static int testdown(float xd, float zd)
27271 {
27272     float f;
27273     if(zd > 0)
27274     {
27275         if(!xd)
27276         {
27277             return 1;
27278         }
27279         f = zd / xd;
27280         return (f > 0.5 || f < -0.5);
27281     }
27282     return 0;
27283 }
27284 
adjust_walk_animation(entity * other)27285 void adjust_walk_animation(entity *other)
27286 {
27287     int dir = 0;
27288     if(self->running)
27289     {
27290         if (validanim(self, ANI_BACKRUN))
27291         {
27292             if(is_in_backrun(self)) ent_set_anim(self, ANI_BACKRUN, 0);
27293             else ent_set_anim(self, ANI_RUN, 0);
27294         }
27295         else ent_set_anim(self, ANI_RUN, 0); // Set to run animation if exists
27296         return;
27297     }
27298 
27299     //reset the walk animation
27300     if(validanim(self, ANI_UP) && ((!other && testup(self->velocity.x, self->velocity.z)) || (other && testup(other->position.x - self->position.x, other->position.z - self->position.z))))
27301     {
27302         common_up_anim(self); //ent_set_anim(self, ANI_UP, 0);
27303         dir = 2;
27304     }
27305     else if(validanim(self, ANI_DOWN) && ((!other && testdown(self->velocity.x, self->velocity.z)) || (other && testdown(other->position.x - self->position.x, other->position.z - self->position.z))))
27306     {
27307         common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0);
27308         dir = 3;
27309     }
27310     else if((self->direction == DIRECTION_RIGHT ? self->velocity.x < 0 : self->velocity.x > 0) && validanim(self, ANI_BACKWALK))
27311     {
27312         common_backwalk_anim(self);    //ent_set_anim(self, ANI_BACKWALK, 0);
27313     }
27314     else
27315     {
27316         common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0);
27317         dir = 1;
27318     }
27319 
27320     if(((self->direction == DIRECTION_RIGHT ? self->velocity.x < 0 : self->velocity.x > 0) && dir == 1) ||
27321             (dir == 2 && self->velocity.z > 0) || (dir == 3 && self->velocity.z < 0) )
27322     {
27323         self->animating = ANIMATING_REVERSE;
27324     }
27325     else
27326     {
27327         self->animating = ANIMATING_FORWARD;
27328     }
27329 }
27330 
27331 
27332 // ai character try to move towards the item
27333 // TODO, check path or entity might get stuck under a wall
common_try_pick(entity * other)27334 int common_try_pick(entity *other)
27335 {
27336     // if there's an item to pick up, move towards it.
27337     float maxspeed = self->modeldata.speed * 1.5;
27338     float dx = diff(self->position.x, other->position.x);
27339     float dz = diff(self->position.z, other->position.z);
27340 
27341     if(other == NULL || self->modeldata.nomove)
27342     {
27343         return 0;
27344     }
27345 
27346     if(!dz && !dx)
27347     {
27348         self->velocity.x = self->velocity.z = 0;
27349         self->destz = self->position.z;
27350         self->destx = self->position.x;
27351     }
27352     else
27353     {
27354         self->velocity.x = maxspeed * dx / (dx + dz);
27355         self->velocity.z = maxspeed * dz / (dx + dz);
27356         self->destx = other->position.x;
27357         self->destz = other->position.z;
27358     }
27359     if(self->position.x > other->position.x)
27360     {
27361         self->velocity.x = -self->velocity.x;
27362     }
27363     if(self->position.z > other->position.z)
27364     {
27365         self->velocity.z = -self->velocity.z;
27366     }
27367 
27368     self->running = 0;
27369 
27370     adjust_walk_animation(other);
27371 
27372     return 1;
27373 }
27374 
27375 #define astarw 640
27376 #define astarh 360
27377 #define starts (astarw*astarh)
27378 
27379 // not so completed pathfinding logic based on a*
27380 // it should be fairly slow due to the complicacy of terrain checking
27381 // and it doesn't always work since walking from wall to wall
27382 // requires jump.
astar(entity * ent,float destx,float destz,float step,s_axis_plane_lateral_float ** wp)27383 int astar(entity *ent, float destx, float destz, float step, s_axis_plane_lateral_float **wp)
27384 {
27385     int (*came_from)[astarw][astarh][2] = malloc(sizeof(*came_from));
27386     unsigned char (*closed)[astarw][astarh] = malloc(sizeof(*closed));
27387     int (*openset)[starts][2] = malloc(sizeof(*openset));
27388     float (*gscore)[astarw][astarh] = malloc(sizeof(*gscore));
27389     float (*hscore)[astarw][astarh] = malloc(sizeof(*hscore));
27390     float (*fscore)[astarw][astarh] = malloc(sizeof(*fscore));
27391     int opensize = 0;
27392     int result = 0, mi = 0;
27393     float tg, minf;
27394     int x, z, i, j, tx, tz, better;
27395     static int vx[8] = {0, 1, 1, 1, 0, -1, -1, -1}, vz[8] = { -1, -1, 0, 1, 1, 1, 0, -1};
27396     static float score[8] = {1.0, 1.4, 1.0, 1.4, 1.0, 1.4, 1.0, 1.4};
27397 
27398     int sx = astarw / 2, sz = astarh / 2;
27399     int dx = sx + (destx - ent->position.x) / step, dz = sz + (destz - ent->position.z) / step;
27400 
27401     *wp = NULL;
27402     if(dx < 0 || dx >= astarw || dz < 0 || dz >= astarh)
27403     {
27404         goto pfclearup;
27405     }
27406     memset(closed, 0, sizeof(*closed));
27407     (*openset)[opensize][0] = sx;
27408     (*openset)[opensize][1] = sz;
27409     opensize++;
27410     memset(came_from, 0, sizeof(*came_from));
27411 
27412     (*gscore)[sx][sz] = 0;
27413     (*hscore)[sx][sz] = diff(dx, sx) + diff(dz, sz);
27414     (*fscore)[sx][sz] = (*gscore)[sx][sz] + (*hscore)[sx][sz];
27415     (*came_from)[sx][sz][0] = -1;
27416 
27417     while(opensize > 0)
27418     {
27419         minf = 9999999;
27420         for(j = 0; j < opensize; j++)
27421         {
27422             x = (*openset)[j][0];
27423             z = (*openset)[j][1];
27424             if((*fscore)[x][z] < minf)
27425             {
27426                 minf = (*fscore)[x][z];
27427                 mi = j;
27428             }
27429         }
27430 
27431         x = (*openset)[mi][0];
27432         z = (*openset)[mi][1];
27433         if(x == dx && z == dz)
27434         {
27435             do
27436             {
27437                 tx = (*came_from)[x][z][0];
27438                 tz = (*came_from)[x][z][1];
27439                 result++;
27440                 x = tx;
27441                 z = tz;
27442             }
27443             while(x >= 0);
27444             *wp = malloc(sizeof(*wp) * result);
27445             tx = (*came_from)[dx][dz][0];
27446             tz = (*came_from)[dx][dz][1];
27447             j = 0;
27448             while(tx >= 0)
27449             {
27450                 (*wp)[j].x = (tx - sx) * step + ent->position.x;
27451                 (*wp)[j].z = (tz - sz) * step + ent->position.z;
27452                 x = (*came_from)[tx][tz][0];
27453                 z = (*came_from)[tx][tz][1];
27454                 tx = x;
27455                 tz = z;
27456                 j++;
27457             }
27458             goto pfclearup;
27459         }
27460 
27461         (*openset)[mi][0] = (*openset)[opensize - 1][0];
27462         (*openset)[mi][1] = (*openset)[opensize - 1][1];
27463 
27464         opensize--;
27465         (*closed)[x][z] = 1;
27466 
27467         for(i = 0; i < 8; i++)
27468         {
27469             tx = x + vx[i];
27470             tz = z + vz[i];
27471 
27472             if(tx < 0 || tx >= astarw || tz < 0 || tz >= astarh)
27473             {
27474                 continue;
27475             }
27476             if((*closed)[tx][tz])
27477             {
27478                 continue;
27479             }
27480 
27481             if(!testmove(ent, (x - sx)*step + ent->position.x, (z - sz)*step + ent->position.z,  (tx - sx)*step + ent->position.x, (tz - sz)*step + ent->position.z))
27482             {
27483                 // (*closed)[tx][tz] = 1; // don't add that to close list just in case the entity can jump
27484                 continue;
27485             }
27486 
27487             tg = (*gscore)[x][z] + score[i];
27488 
27489             for(j = 0; j < opensize; j++)
27490             {
27491                 if((*openset)[j][0] == tx && (*openset)[j][1] == tz)
27492                 {
27493                     break;
27494                 }
27495             }
27496 
27497             if(j == opensize)
27498             {
27499                 (*openset)[opensize][0] = tx;
27500                 (*openset)[opensize][1] = tz;
27501                 opensize++;
27502                 better = 1;
27503             }
27504             else if (tg < (*gscore)[tx][tz])
27505             {
27506                 better = 1;
27507             }
27508             else
27509             {
27510                 better = 0;
27511             }
27512 
27513             if(better)
27514             {
27515                 (*came_from)[tx][tz][0] = x;
27516                 (*came_from)[tx][tz][1] = z;
27517                 (*gscore)[tx][tz] = tg;
27518                 (*hscore)[tx][tz] = diff(tx, dx) + diff(tz, dz);
27519                 (*fscore)[tx][tz] = (*gscore)[tx][tz] + (*hscore)[tx][tz];
27520             }
27521         }
27522     }
27523 
27524 pfclearup:
27525     if(came_from)
27526     {
27527         free(came_from);
27528     }
27529     came_from = NULL;
27530     if(closed)
27531     {
27532         free(closed);
27533     }
27534     closed = NULL;
27535     if(openset)
27536     {
27537         free(openset);
27538     }
27539     openset = NULL;
27540     if(gscore)
27541     {
27542         free(gscore);
27543     }
27544     gscore = NULL;
27545     if(hscore)
27546     {
27547         free(hscore);
27548     }
27549     hscore = NULL;
27550     if(fscore)
27551     {
27552         free(fscore);
27553     }
27554     fscore = NULL;
27555 
27556     return result;
27557 }
27558 
27559 
27560 // use this after a wall checking meets
27561 // wall sliding code
27562 // whichside:
27563 //      0
27564 //  1       3
27565 //      2
adjustdirection(float coords[],float offx,float offz,float ox,float oz,float xdir,float zdir,float * cxdir,float * czdir)27566 int adjustdirection(float coords[], float offx, float offz, float ox, float oz, float xdir, float zdir, float *cxdir, float *czdir)
27567 {
27568     float x[4], z[4];
27569     int whichside, i;
27570     float a;
27571 
27572     for(i = 0; i < 4; i++)
27573     {
27574         x[i] = coords[2 + i] + coords[0] + offx;
27575     }
27576     z[1] = z[3] = coords[1] + offz;
27577     z[0] = z[2] = z[1] - coords[6];
27578 
27579     if(oz <= z[0])
27580     {
27581         whichside = 0;
27582     }
27583     else if(oz >= z[1])
27584     {
27585         whichside = 2;
27586     }
27587     else if(ox < x[2])
27588     {
27589         whichside = 1;
27590     }
27591     else
27592     {
27593         whichside = 3;
27594     }
27595 
27596     if(whichside == 0 || whichside == 2)
27597     {
27598         *cxdir = xdir;
27599         *czdir = 0;
27600     }
27601     else
27602     {
27603         if((x[0] == x[1] && whichside == 1) || (x[2] == x[3] && whichside == 3))
27604         {
27605             *cxdir = 0;
27606             *czdir = zdir;
27607         }
27608         else
27609         {
27610             if(whichside == 1)
27611             {
27612                 a = (z[1] - z[0]) / (x[1] - x[0]);
27613             }
27614             else
27615             {
27616                 a = (z[3] - z[2]) / (x[3] - x[2]);
27617             }
27618 
27619             *cxdir = xdir + zdir / a;
27620             *czdir = a * xdir + zdir;
27621 
27622             a = (ABS(xdir) + ABS(zdir)) / (ABS(*cxdir) + ABS(*czdir)) ;
27623             *cxdir *= a;
27624             *czdir *= a;
27625         }
27626     }
27627     //printf("%f, %f, %f, %f, %d\n", xdir, zdir, *cxdir, *czdir, whichside);
27628     return whichside;
27629 }
27630 
27631 // adjust walk speed for entity assuming it walks straight forward
27632 // x, z - current position
27633 // tx, tz - target position
27634 // speed - max speed
27635 // xdir, zdir - return values
adjustspeed(float speed,float x,float z,float tx,float tz,float * xdir,float * zdir)27636 void adjustspeed(float speed, float x, float z, float tx, float tz, float *xdir, float *zdir)
27637 {
27638     float xd, zd;
27639     float dx = diff(tx, x);
27640     float dz = diff(tz, z) * 2;
27641 
27642     if(dx > dz)
27643     {
27644         xd = speed;
27645         zd = xd / dx * dz;
27646     }
27647     else if(dz > dx)
27648     {
27649         zd = speed;
27650         xd = zd / dz * dx;
27651     }
27652     else if(dx)
27653     {
27654         xd = zd = speed;
27655     }
27656     else
27657     {
27658         xd = zd = 0;
27659     }
27660 
27661     if(tx < x)
27662     {
27663         xd = -xd;
27664     }
27665     if(tz < z)
27666     {
27667         zd = -zd;
27668     }
27669 
27670     zd /= 2;
27671 
27672     *xdir = xd;
27673     *zdir = zd;
27674 
27675 }
27676 
checkpathblocked()27677 int checkpathblocked()
27678 {
27679     float x, z, r;
27680     int aitype, wpc;
27681     entity *target;
27682 	s_axis_plane_lateral_float *wp;
27683     if(self->modeldata.nomove)
27684     {
27685         return 0;
27686     }
27687     if(self->stalltime >= _time)
27688     {
27689         aitype = self->modeldata.aimove;
27690         if(self->modeldata.subtype == SUBTYPE_CHASE)
27691         {
27692             aitype |= AIMOVE1_CHASE;
27693         }
27694 
27695         //be moo tolerable to PLAYER_MAX_Z and PLAYER_MIN_Z
27696         if((self->modeldata.subject_to_maxz && self->velocity.z > 0 && !self->velocity.x && self->velocity.z + self->position.z > PLAYER_MAX_Z) ||
27697                 (self->modeldata.subject_to_minz && self->velocity.z < 0 && !self->velocity.x && self->velocity.z + self->position.z < PLAYER_MIN_Z) )
27698         {
27699             self->velocity.z = -self->velocity.z;
27700             self->pathblocked = 0;
27701             self->destz = self->velocity.z > 0 ? (PLAYER_MIN_Z + videomodes.vRes / 10) : (PLAYER_MAX_Z - videomodes.vRes / 10);
27702             adjust_walk_animation(NULL);
27703             return 1;
27704         }
27705 
27706         if(self->pathblocked > 40 || (self->pathblocked > 20 && (aitype & (AIMOVE1_CHASEX | AIMOVE1_CHASEZ | AIMOVE1_CHASE))))
27707         {
27708             if(self->modeldata.pathfindstep > 0)
27709             {
27710                 target = normal_find_target(-1, 0);
27711 
27712                 if(target)
27713                 {
27714                     //printf("pathfind: (%f %f)-(%f %f) %d steps\n", self->position.x, self->position.z, self->destx, self->destz, pathfind(self, self->destx, self->destz));
27715                     if((wpc = astar(self, target->position.x, target->position.z, self->modeldata.pathfindstep, &wp)) > 0)
27716                     {
27717                         //printf("wp %d\n", wp);
27718                         self->numwaypoints = wpc;
27719                         if(self->waypoints)
27720                         {
27721                             free(self->waypoints);
27722                         }
27723                         self->waypoints = wp;
27724                         self->destx = self->waypoints[self->numwaypoints - 1].x;
27725                         self->destz = self->waypoints[self->numwaypoints - 1].z;
27726                         self->numwaypoints--;
27727                         self->pathblocked = 0;
27728                         return 1;
27729                     }
27730                 }
27731             }
27732 
27733             x = self->velocity.x;
27734             z = self->velocity.z;
27735             if(x > 0)
27736             {
27737                 x = self->modeldata.speed;
27738             }
27739             else if(x < 0)
27740             {
27741                 x = -self->modeldata.speed;
27742             }
27743             if(z > 0)
27744             {
27745                 z = self->modeldata.speed / 2;
27746             }
27747             else if(z < 0)
27748             {
27749                 z = -self->modeldata.speed / 2;
27750             }
27751             r = randf(1);
27752             if(r > 0.6f)
27753             {
27754                 self->velocity.z = x;
27755                 self->velocity.x = -z;
27756             }
27757             else if(r > 0.2f)
27758             {
27759                 self->velocity.z = -x;
27760                 self->velocity.x = z;
27761             }
27762             else
27763             {
27764                 self->velocity.z = (1.0f - randf(2)) * self->modeldata.speed / 2;
27765                 self->velocity.x = (1.0f - randf(2)) * self->modeldata.speed;
27766             }
27767             self->running = 0; // TODO: re-adjust walk speed
27768             self->stalltime = _time + GAME_SPEED / 2;
27769             adjust_walk_animation(NULL);
27770             self->pathblocked = 0;
27771 
27772             if(self->velocity.z > 0)
27773             {
27774                 self->destz = self->position.z + 40;
27775             }
27776             else if(self->velocity.z < 0)
27777             {
27778                 self->destz = self->position.z - 40;
27779             }
27780             else
27781             {
27782                 self->destz = self->position.z;
27783             }
27784             if(self->velocity.x > 0)
27785             {
27786                 self->destx = self->position.x + 40;
27787             }
27788             else if(self->velocity.x < 0)
27789             {
27790                 self->destx = self->position.x - 40;
27791             }
27792             else
27793             {
27794                 self->destx = self->position.x;
27795             }
27796 
27797             return 1;
27798 
27799         }
27800     }
27801     return 0;
27802 }
27803 
27804 
27805 // this is the most aggressive aimove pattern
27806 // the entity will try get in and attack at anytime
27807 // though the range depends on what attack you setup
common_try_chase(entity * target,int dox,int doz)27808 int common_try_chase(entity *target, int dox, int doz)
27809 {
27810     // start chasing the target
27811     float dx, dz, range;
27812     int randomatk;
27813 
27814     self->running = 0;
27815 
27816     //adjustspeed(self->modeldata.speed, self->position.x, self->position.z, self->position.x + self->velocity.x, self->position.z + self->velocity.z, &self->velocity.x, &self->velocity.z);
27817 
27818     if(target == NULL || self->modeldata.nomove)
27819     {
27820         return 0;
27821     }
27822 
27823     randomatk = pick_random_attack(NULL, 0);
27824 
27825     if(randomatk >= 0)
27826     {
27827         range = (self->modeldata.animation[randomatk]->range.x.min + self->modeldata.animation[randomatk]->range.x.max) / 2;
27828         //printf("range picked: ani %d, range %f\n", randomatk, range);
27829         if(range < 0)
27830         {
27831             range = self->modeldata.grabdistance;
27832         }
27833         else if(range > videomodes.hRes / 4)
27834         {
27835             range = videomodes.hRes / 4;
27836         }
27837     }
27838     else
27839     {
27840         range = self->modeldata.grabdistance;
27841     }
27842 
27843     if(dox)
27844     {
27845         if(self->position.x > target->position.x)
27846         {
27847             self->destx = target->position.x + range - 1;
27848         }
27849         else
27850         {
27851             self->destx = target->position.x - range + 1;
27852         }
27853         dx = diff(self->position.x, self->destx);
27854 
27855         if(dx > 150 && validanim(self, ANI_RUN))
27856         {
27857             self->velocity.x = self->modeldata.runspeed;
27858             self->running = 1;
27859         }
27860         else
27861         {
27862             self->velocity.x = self->modeldata.speed;
27863         }
27864         if(self->destx < self->position.x)
27865         {
27866             self->velocity.x = -self->velocity.x;
27867         }
27868     }
27869 
27870     if(doz)
27871     {
27872         self->destz = target->position.z ;
27873         dz = diff(self->position.z, self->destz);
27874 
27875         if(dz > 100 && self->modeldata.runupdown && validanim(self, ANI_RUN))
27876         {
27877             self->velocity.z = self->modeldata.runspeed / 2;
27878             self->running = 1;
27879         }
27880         else
27881         {
27882             self->velocity.z = self->modeldata.speed / 2;
27883         }
27884         if(self->destz < self->position.z)
27885         {
27886             self->velocity.z = -self->velocity.z;
27887         }
27888     }
27889 
27890     return 1;
27891 }
27892 
27893 //may be used many times, so make a function
27894 // minion follow his owner
common_try_follow(entity * target,int dox,int doz)27895 int common_try_follow(entity *target, int dox, int doz)
27896 {
27897     // start chasing the target
27898     float dx, dz, distance;
27899     int mx, mz;
27900     int facing;
27901 
27902     //target = self->parent;
27903     if(target == NULL || self->modeldata.nomove)
27904     {
27905         return 0;
27906     }
27907     distance = (float)((validanim(self, ANI_IDLE)) ? self->modeldata.animation[ANI_IDLE]->range.x.min : 100);
27908 
27909     if(distance <= 0)
27910     {
27911         distance = 100.0;
27912     }
27913 
27914     facing = (self->direction == DIRECTION_RIGHT ? self->position.x < target->position.x : self->position.x > target->position.x);
27915 
27916     dx = diff(self->position.x, target->position.x);
27917     dz = diff(self->position.z, target->position.z);
27918 
27919     if(dox && dx < distance)
27920     {
27921         self->velocity.x = 0;
27922         mx = 0;
27923     }
27924     else
27925     {
27926         mx = 1;
27927     }
27928 
27929     if(doz && dz < distance / 2)
27930     {
27931         self->velocity.z = 0;
27932         mz = 0;
27933     }
27934     else
27935     {
27936         mz = 1;
27937     }
27938 
27939     if(dox && mx)
27940     {
27941         if(facing && dx > 200 && validanim(self, ANI_RUN))
27942         {
27943             self->velocity.x = self->modeldata.runspeed;
27944             self->running = 1;
27945         }
27946         else
27947         {
27948             self->velocity.x = self->modeldata.speed;
27949             self->running = 0;
27950         }
27951         if(self->position.x > target->position.x)
27952         {
27953             self->velocity.x = -self->velocity.x;
27954         }
27955         self->destx = target->position.x;
27956     }
27957 
27958     if(doz && mz)
27959     {
27960         if(facing && dx > 200 && self->modeldata.runupdown && validanim(self, ANI_RUN))
27961         {
27962             self->velocity.z = self->modeldata.runspeed / 2;
27963             self->running = 1;
27964         }
27965         else
27966         {
27967             self->velocity.z = self->modeldata.speed / 2;
27968             self->running = 0; // not right, to be modified
27969         }
27970         if(self->position.z > target->position.z)
27971         {
27972             self->velocity.z = -self->velocity.z;
27973         }
27974         self->destz = target->position.z;
27975     }
27976 
27977 
27978     return 1;
27979 }
27980 
27981 // try to avoid the target
27982 // used by 'avoid avoidz avoidx
27983 // Basic logic: the entity walk within a min distance and a max distance from the target
common_try_avoid(entity * target,int dox,int doz)27984 int common_try_avoid(entity *target, int dox, int doz)
27985 {
27986     float dx, dz;
27987     float maxdz, mindz, maxdx, mindx;
27988     int randomatk;
27989 
27990     if(target == NULL || self->modeldata.nomove)
27991     {
27992         return 0;
27993     }
27994 
27995     dx = diff(self->position.x, target->position.x);
27996     dz = diff(self->position.z, target->position.z);
27997 
27998     randomatk = pick_random_attack(NULL, 0);
27999 
28000     if((rand32() & 15) < 8 && randomatk >= 0)
28001     {
28002         maxdx = self->modeldata.animation[randomatk]->range.x.max - self->modeldata.speed;
28003         if(maxdx < videomodes.hRes / 5)
28004         {
28005             maxdx = videomodes.hRes / 5;
28006         }
28007         mindx = maxdx - 10;
28008         maxdz = self->modeldata.animation[randomatk]->range.z.max - self->modeldata.speed;
28009         if(maxdz < videomodes.vRes / 5)
28010         {
28011             maxdz = videomodes.vRes / 5;
28012         }
28013         mindz = maxdz - 10;
28014     }
28015     else
28016     {
28017         mindx = videomodes.hRes / 3;
28018         maxdx = videomodes.hRes / 2;
28019         mindz = videomodes.vRes / 3;
28020         maxdz = videomodes.vRes / 2;
28021     }
28022 
28023     if(dox)
28024     {
28025         if(self->position.x < screenx)
28026         {
28027             self->velocity.x = self->modeldata.speed;
28028             self->destx = screenx + videomodes.hRes / 12.0;
28029         }
28030         else if(self->position.x > screenx + videomodes.hRes)
28031         {
28032             self->velocity.x = -self->modeldata.speed;
28033             self->destx = screenx + videomodes.hRes * 11.0 / 12.0;
28034         }
28035         else if(dx < mindx)
28036         {
28037             self->velocity.x = (self->position.x < target->position.x) ? (-self->modeldata.speed) : self->modeldata.speed;
28038             self->destx = (self->position.x < target->position.x) ? (target->position.x - maxdx) : (target->position.x + maxdx);
28039         }
28040         else if (dx > maxdx)
28041         {
28042             self->velocity.x = (self->position.x < target->position.x) ? self->modeldata.speed : (-self->modeldata.speed);
28043             self->destx = (self->position.x < target->position.x) ? (target->position.x - mindx) : (target->position.x + mindx);
28044         }
28045         else
28046         {
28047             self->velocity.x = 0;
28048             self->destx = self->position.x;
28049         }
28050     }
28051 
28052     if(doz)
28053     {
28054         if(self->position.z < screeny)
28055         {
28056             self->velocity.z = self->modeldata.speed / 2;
28057             self->destz = screeny +  videomodes.vRes / 12.0;
28058         }
28059         else if(self->position.z > screeny + videomodes.vRes)
28060         {
28061             self->velocity.z = -self->modeldata.speed / 2;
28062             self->destz = screeny +  videomodes.vRes * 11.0 / 12.0;
28063         }
28064         else if(dz < mindz)
28065         {
28066             self->velocity.z = (self->position.z < target->position.z) ? (-self->modeldata.speed / 2) : (self->modeldata.speed / 2);
28067             self->destz = (self->position.z < target->position.z) ? (target->position.z - maxdz) : (target->position.z + maxdz);
28068         }
28069         else if(dz > maxdz)
28070         {
28071             self->velocity.z = (self->position.z < target->position.z) ? (self->modeldata.speed / 2) : (-self->modeldata.speed / 2);
28072             self->destz = (self->position.z < target->position.z) ? (target->position.z - mindz) : (target->position.z + mindz);
28073         }
28074         else
28075         {
28076             self->velocity.z = 0;
28077             self->destz = self->position.z;
28078         }
28079     }
28080 
28081     return 1;
28082 }
28083 
28084 //  wander completely and ignore the target
28085 // this ai pattern only works when you use aimove wander,
28086 // if you mix wander with other patterns like chase or avoid
28087 // this pattern is not triggered
common_try_wandercompletely(int dox,int doz)28088 int common_try_wandercompletely(int dox, int doz)
28089 {
28090     int rnum;
28091 
28092     if(self->modeldata.nomove)
28093     {
28094         return 0;
28095     }
28096 
28097     if(dox)
28098     {
28099         rnum = rand32() & 15;
28100         if(rnum < 4)
28101         {
28102             self->velocity.x = -self->modeldata.speed;
28103         }
28104         else if(rnum > 11)
28105         {
28106             self->velocity.x = self->modeldata.speed;
28107         }
28108         else
28109         {
28110             self->velocity.x = 0;
28111         }
28112         if( self->position.x < screenx - 10)
28113         {
28114             self->velocity.x = self->modeldata.speed;
28115         }
28116         else if(self->position.x > screenx + videomodes.hRes + 10)
28117         {
28118             self->velocity.x = -self->modeldata.speed;
28119         }
28120 
28121         if(self->velocity.x > 0)
28122         {
28123             self->destx = self->position.x + videomodes.hRes / 5;
28124         }
28125         else if(self->velocity.x < 0)
28126         {
28127             self->destx = self->position.x - videomodes.hRes / 5;
28128         }
28129         else
28130         {
28131             self->destx = self->position.x;
28132         }
28133 
28134     }
28135     if(doz)
28136     {
28137         rnum = rand32() & 15;
28138         if(rnum < 4)
28139         {
28140             self->velocity.z = -self->modeldata.speed / 2;
28141         }
28142         else if(rnum > 11)
28143         {
28144             self->velocity.z = self->modeldata.speed / 2;
28145         }
28146         else
28147         {
28148             self->velocity.z = 0;
28149         }
28150         if(self->position.z < screeny - 10)
28151         {
28152             self->velocity.z = self->modeldata.speed / 2;
28153         }
28154         else if(self->position.z > screeny + videomodes.vRes + 10)
28155         {
28156             self->velocity.z = -self->modeldata.speed / 2;
28157         }
28158 
28159         if(self->velocity.z > 0)
28160         {
28161             self->destz = self->position.z + videomodes.vRes / 5;
28162         }
28163         else if(self->velocity.z < 0)
28164         {
28165             self->destz = self->position.z - videomodes.vRes / 5;
28166         }
28167         else
28168         {
28169             self->destz = self->position.z;
28170         }
28171 
28172     }
28173 
28174     return 1;
28175 
28176 }
28177 /*
28178 int assume_safe_distance(entity* target, int ani, int* minx, int* maxx, int* minz, int* maxz)
28179 {
28180 	int f, set = 0;
28181 	short tminx, tmaxx, tminz, tmaxz;
28182 	s_anim* ta;
28183 	short* coords;
28184 	if(validanim(target, ani)){
28185 		ta = target->modeldata.animation[ani];
28186 		*minx = *minz = 9999;
28187 		*maxx = *maxz = -9999;
28188 		if(ta->collision_attack){
28189 			for(f=0; f<ta->numframes; f++){
28190 				if(!ta->collision_attack[f]) continue;
28191 				coords = ta->collision_attack[f]->coords;
28192 				if(target->direction == DIRECTION_RIGHT) {
28193 					tminx = coords[0];
28194 					tmaxx = coords[2];
28195 				}else{
28196 					tminx = -coords[2];
28197 					tmaxx = -coords[0];
28198 				}
28199 				tminz = -coords[4];
28200 				tmaxz = coords[4];
28201 				if(tminx<*minx)
28202 					*minx = tminx;
28203 				if(tminz<*minz)
28204 					*minz = tminz;
28205 				if(tmaxx>*maxx)
28206 					*maxx = tmaxx;
28207 				if(tmaxz>*maxz)
28208 					*maxz = tmaxz;
28209 
28210 				set = 1;
28211 			}
28212 
28213 			if(set && self->animation->collision_body->coords && self->animation->collision_body[self->animpos]->coords){
28214 				coords = self->animation->collision_body[self->animpos]->coords;
28215 				*minx -= coords[2] - coords[0];
28216 				*minz -= coords[4];
28217 				*maxx += coords[2] - coords[0];
28218 				*maxz += coords[4];
28219 			}
28220 
28221 			return set;
28222 		}
28223 	}
28224 
28225 	return 0;
28226 
28227 }
28228 */
28229 // for normal and default ai patttern
28230 // the entity is not actually wandering
28231 // they just go around the target and get close
28232 // occasionally to attack
common_try_wander(entity * target,int dox,int doz)28233 int common_try_wander(entity *target, int dox, int doz)
28234 {
28235     int walk = 0, behind, grabd, mod;
28236 
28237     float diffx, diffz, //distance from target
28238           returndx, returndz, //how far should keep from target
28239           borderdx, borderdz, //how far should keep offscreen
28240           mindx, mindz;// don't walk into the target
28241     int rnum = rand32() & 15, t, randomatk;
28242 
28243     if(!target || self->modeldata.nomove)
28244     {
28245         return 0;
28246     }
28247 
28248     diffx = diff(self->position.x, target->position.x);
28249     diffz = diff(self->position.z, target->position.z);
28250     behind = ((self->position.x < target->position.x) == target->direction);
28251     grabd = self->modeldata.grabdistance;
28252     //when entity is behind the target, it has a greater chance to go after the target
28253     if(behind && diffx < grabd * 4 && diffz < grabd) //right behind, go for it
28254     {
28255         t = 13;
28256     }
28257     else if(behind)   // only behind, half chance
28258     {
28259         t = 7;
28260     }
28261     else    // otherwise, 1/8 chance
28262     {
28263         t = 2;
28264     }
28265 
28266     if(behind && target->attacking != ATTACKING_NONE)
28267     {
28268         t += 5;
28269     }
28270 
28271     // could use this to replace the completely wander ai
28272     if(dox != doz)
28273     {
28274         t = 0;
28275     }
28276 
28277     if(rnum < t) //chase target
28278     {
28279         returndx = videomodes.hRes / 4;
28280         returndz = videomodes.vRes / 8;
28281     }
28282     else   // only chase when too far away
28283     {
28284         returndx = videomodes.hRes * 0.6;
28285         returndz = videomodes.vRes * 0.4;
28286     }
28287     if(rnum > 7)
28288     {
28289         borderdx = videomodes.hRes / 8;
28290         borderdz = videomodes.vRes / 8;
28291     }
28292     else
28293     {
28294         borderdx = borderdz = 0;
28295     }
28296 
28297     randomatk = pick_random_attack(NULL, 0);
28298 
28299     if(randomatk >= 0)
28300     {
28301         mindx = self->modeldata.animation[randomatk]->range.x.max - (self->modeldata.animation[randomatk]->range.x.max - self->modeldata.animation[randomatk]->range.x.min) / 4 - 1;
28302     }
28303     else
28304     {
28305         mindx = (!behind && target->attacking != ATTACKING_NONE) ? grabd * 3 : grabd * 1.2;
28306     }
28307     mindz = grabd / 4;
28308 
28309     mod = ((int)(_time / (videomodes.hRes / self->modeldata.speed)) + 1000 + self->energy_state.health_current / 3 + self->pathblocked + self->modeldata.aggression / 10) % 4;
28310     if(mod < 0)
28311     {
28312         mod = -mod;
28313     }
28314     //if ((self->sortid / 100) % 2)
28315     if (rand32() % 2)
28316     {
28317         mod = 3 - mod;
28318     }
28319 
28320     if(dox)
28321     {
28322         if(self->position.x < screenx - borderdx)
28323         {
28324             self->velocity.x = self->modeldata.speed;
28325             self->destx = screenx + videomodes.hRes / 8.0;
28326             walk = 1;
28327         }
28328         else if (self->position.x > screenx + videomodes.hRes + borderdx)
28329         {
28330             self->velocity.x = -self->modeldata.speed;
28331             self->destx = screenx + videomodes.hRes * 7.0 / 8.0;
28332             walk = 1;
28333         }
28334         else if(diffx > returndx)
28335         {
28336             self->velocity.x = (self->position.x > target->position.x) ? -self->modeldata.speed : self->modeldata.speed;
28337             self->destx = (self->position.x > target->position.x) ? (target->position.x + mindx) : (target->position.x - mindx);
28338             walk = 1;
28339         }
28340         else
28341         {
28342             switch(mod)
28343             {
28344             case 0:
28345                 self->destx = target->position.x + grabd;
28346                 break;
28347             case 2:
28348                 self->destx = target->position.x - grabd;
28349                 break;
28350             case 1:
28351                 self->destx = target->position.x + videomodes.hRes * 0.4 + (self->energy_state.health_current / 3 % 20);
28352                 break;
28353             case 3:
28354                 self->destx = target->position.x - videomodes.hRes * 0.4 - (self->energy_state.health_current / 3 % 20);
28355                 break;
28356             }
28357             self->velocity.x = self->position.x > self->destx ? -self->modeldata.speed : self->modeldata.speed;
28358             walk = 1;
28359             //printf("mod x %d\n", mod);
28360         }
28361     }
28362 
28363     if(doz)
28364     {
28365         if(self->position.z < screeny - borderdz)
28366         {
28367             self->velocity.z = self->modeldata.speed / 2;
28368             self->destz = screeny + videomodes.vRes / 12.0;
28369             walk |= 1;
28370         }
28371         else if (self->position.z > screeny + videomodes.vRes + borderdz)
28372         {
28373             self->velocity.z = -self->modeldata.speed / 2;
28374             self->destz = screeny + videomodes.vRes * 11.0 / 12.0;
28375             walk |= 1;
28376         }
28377         else if(diffz > returndz)
28378         {
28379             self->velocity.z = (self->position.z > target->position.z) ? -self->modeldata.speed / 2 : self->modeldata.speed / 2;
28380             self->destz = (self->position.z > target->position.z) ? (target->position.z + mindz) : (target->position.z - mindz);
28381             walk |= 1;
28382         }
28383         else
28384         {
28385             switch(mod)
28386             {
28387             case 1:
28388                 self->destz = target->position.z + grabd / 2;
28389                 break;
28390             case 3:
28391                 self->destz = target->position.z - grabd / 2;
28392                 break;
28393             case 2:
28394                 self->destz = target->position.z + MIN((PLAYER_MAX_Z - PLAYER_MIN_Z), videomodes.vRes) * 0.25 + (self->energy_state.health_current / 3 % 5);
28395                 break;
28396             case 0:
28397                 self->destz = target->position.z - MIN((PLAYER_MAX_Z - PLAYER_MIN_Z), videomodes.vRes) * 0.25 - (self->energy_state.health_current / 3 % 5);
28398                 break;
28399             }
28400             self->velocity.z = self->position.z > self->destz ? -self->modeldata.speed / 2 : self->modeldata.speed / 2;
28401             walk |= 1;
28402             //printf("mod z %d\n", mod);
28403         }
28404     }
28405 
28406     return walk;
28407 }
28408 
28409 // Caskey, Damon V.
28410 // 208-04-09
28411 //
28412 // Set up attack by item and execute didhit script as if item "hit" collector
28413 // to allow easy item scripting.
do_item_script(entity * ent,entity * item)28414 void do_item_script(entity *ent, entity *item)
28415 {
28416     s_collision_attack attack;
28417     attack = emptyattack;
28418     attack.attack_type = ATK_ITEM;
28419 
28420     execute_didhit_script(item, ent, &attack, 0);
28421 }
28422 
28423 //A.I chracter pickup an item
common_pickupitem(entity * other)28424 void common_pickupitem(entity *other)
28425 {
28426     int pickup = 0;
28427     //weapons
28428     if(self->weapent == NULL && isSubtypeWeapon(other) && validanim(self, ANI_GET))
28429     {
28430         self->takeaction = common_get;
28431         dropweapon(0);  //don't bother dropping the previous one though, scine it won't pickup another
28432         self->weapent = other;
28433         set_weapon(self, other->modeldata.weapnum, 0);
28434         set_getting(self);
28435         self->velocity.x = self->velocity.z = 0; //stop moving
28436         if(self->modeldata.animal)  // UTunnels: well, ride, not get. :)
28437         {
28438             self->direction = other->direction;
28439             self->position.x = other->position.x;
28440             self->position.z = other->position.z;
28441         }
28442         other->nextanim = other->nextthink = _time + GAME_SPEED * 999999;
28443         ent_set_anim(self, ANI_GET, 0);
28444         pickup = 1;
28445     }
28446     // projectiles
28447     else if(self->weapent == NULL && isSubtypeProjectile(other) && validanim(self, ANI_GET))
28448     {
28449         self->takeaction = common_get;
28450         dropweapon(0);
28451         self->weapent = other;
28452         set_getting(self);
28453         self->velocity.x = self->velocity.z = 0; //stop moving
28454         other->nextanim = other->nextthink = _time + GAME_SPEED * 999999;
28455         ent_set_anim(self, ANI_GET, 0);
28456         pickup = 1;
28457     }
28458     // other items
28459     else if(! isSubtypeWeapon(other) && ! isSubtypeProjectile(other) )
28460     {
28461         if(validanim(self, ANI_GET) && !isSubtypeTouch(other))
28462         {
28463             self->takeaction = common_get;
28464             set_getting(self);
28465             self->velocity.x = self->velocity.z = 0; //stop moving
28466             ent_set_anim(self, ANI_GET, 0);
28467         }
28468         if(other->energy_state.health_current)
28469         {
28470             self->energy_state.health_current += other->energy_state.health_current;
28471             if(self->energy_state.health_current > self->modeldata.health)
28472             {
28473                 self->energy_state.health_current = self->modeldata.health;
28474             }
28475             other->energy_state.health_current = 0;
28476             //if(SAMPLE_GET >= 0) sound_play_sample(SAMPLE_GET, 0, savedata.effectvol,savedata.effectvol, 100);
28477         }
28478         // else if, TODO: other effects
28479         // kill that item
28480         other->takeaction = suicide;
28481         other->nextthink = _time + GAME_SPEED * 3;
28482         pickup = 1;
28483     }
28484     // hide it
28485     if(pickup)
28486     {
28487         do_item_script(self, other);
28488 
28489         other->position.z = ITEM_HIDE_POSITION_Z;
28490     }
28491 }
28492 
28493 // for old bikers
biker_move()28494 int biker_move()
28495 {
28496 
28497     if((self->direction == DIRECTION_RIGHT) ? (self->position.x > advancex + (videomodes.hRes + 200)) : (self->position.x < advancex - 200))
28498     {
28499         self->direction = !self->direction;
28500         self->attack_id_outgoing = 0;
28501         self->position.z = (float)(PLAYER_MIN_Z + randf((float)(PLAYER_MAX_Z - PLAYER_MIN_Z)));
28502         if(SAMPLE_BIKE >= 0)
28503         {
28504             sound_play_sample(SAMPLE_BIKE, 0, savedata.effectvol, savedata.effectvol, 100);
28505         }
28506         if(self->modeldata.speed)
28507         {
28508             self->velocity.x = (self->direction == DIRECTION_RIGHT) ? (self->modeldata.speed) : (-self->modeldata.speed);
28509         }
28510         else
28511         {
28512             self->velocity.x = (self->direction == DIRECTION_RIGHT) ? ((float)1.7 + randf((float)0.6)) : (-((float)1.7 + randf((float)0.6)));
28513         }
28514     }
28515 
28516     return 1;
28517 }
28518 
28519 // for common arrow types
arrow_move()28520 int arrow_move()
28521 {
28522     float dx;
28523     float dz;
28524     float maxspeed;
28525     entity *target = NULL;
28526 
28527     // new subtype chase
28528     if(self->modeldata.subtype == SUBTYPE_CHASE)
28529     {
28530         target = homing_find_target(self->modeldata.hostile);
28531 
28532         if(target)
28533         {
28534             if(!self->modeldata.noflip)
28535             {
28536                 self->direction = (target->position.x > self->position.x);
28537             }
28538             // start chasing the target
28539             dx = diff(self->position.x, target->position.x);
28540             dz = diff(self->position.z, target->position.z);
28541             maxspeed = self->modeldata.speed * 1.5;
28542 
28543             if(!dz && !dx)
28544             {
28545                 self->velocity.x = self->velocity.z = 0;
28546             }
28547             else
28548             {
28549                 self->velocity.x = maxspeed * dx / (dx + dz);
28550                 self->velocity.z = maxspeed * dz / (dx + dz);
28551             }
28552             if(self->direction == DIRECTION_LEFT)
28553             {
28554                 self->velocity.x = -self->velocity.x;
28555             }
28556             if(self->position.z > target->position.z)
28557             {
28558                 self->velocity.z = -self->velocity.z;
28559             }
28560         }
28561         else
28562         {
28563             if(!self->velocity.x && !self->velocity.z)
28564             {
28565                 if(self->direction == DIRECTION_LEFT)
28566                 {
28567                     self->velocity.x = -self->modeldata.speed;
28568                 }
28569                 else if(self->direction == DIRECTION_RIGHT)
28570                 {
28571                     self->velocity.x = self->modeldata.speed;
28572                 }
28573             }
28574         }
28575         if(!self->modeldata.nomove)
28576         {
28577             if(target && self->position.z > target->position.z && validanim(self, ANI_UP))
28578             {
28579                 common_up_anim(self);    //ent_set_anim(self, ANI_UP, 0);
28580             }
28581             else if(target && target->position.z > self->position.z && validanim(self, ANI_DOWN))
28582             {
28583                 common_down_anim(self);    //ent_set_anim(self, ANI_DOWN, 0);
28584             }
28585             else if(validanim(self, ANI_WALK))
28586             {
28587                 common_walk_anim(self);    //ent_set_anim(self, ANI_WALK, 0);
28588             }
28589             else
28590             {
28591                 if(validanim(self, ANI_IDLE)) ent_set_anim(self, ANI_IDLE, 0);
28592             }
28593         }
28594     }
28595     else
28596     {
28597         // Now projectiles can have custom speeds
28598         if(self->direction == DIRECTION_LEFT)
28599         {
28600             self->velocity.x = -self->modeldata.speed;
28601         }
28602         else if(self->direction == DIRECTION_RIGHT)
28603         {
28604             self->velocity.x = self->modeldata.speed;
28605         }
28606     }
28607 
28608     if(level)
28609     {
28610         // Bounce off walls or platforms.
28611         projectile_wall_deflect(self);
28612     }
28613 
28614     if(self->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR)
28615     {
28616         self->autokill |= AUTOKILL_ANIMATION_COMPLETE;
28617     }
28618 
28619     return 1;
28620 }
28621 
28622 // Caskey, Damon V.
28623 // 2018-04-07
28624 //
28625 // Find out if there is a wall blocking target entity, and
28626 // if so return its array key. Returns 0 if no blocking
28627 // wall is found.
check_block_wall(entity * entity)28628 int check_block_wall(entity *entity)
28629 {
28630     int wall = -1;
28631 
28632     // Target entity affected by walls?
28633     if(entity->modeldata.subject_to_wall)
28634     {
28635         // Get wall number at our X and Z axis (if any).
28636         wall = checkwall_index(entity->position.x, entity->position.z);
28637 
28638         // Did we find a wall?
28639         if (wall >= 0)
28640         {
28641             // Compare wall height to our current
28642             // Y axis position. If the wall is
28643             // higher, then it's blocking our way.
28644             // We can return the wall number.
28645             if(entity->position.y < level->walls[wall].height)
28646             {
28647                 return wall;
28648             }
28649         }
28650     }
28651 
28652     // Got this far? Then there's no wall blocking our way.
28653     return wall;
28654 }
28655 
28656 // Caskey, Damon V.
28657 // 2018-04-07
28658 //
28659 // Return obstacle entity that is blocking target
28660 // entity (if any). Returns NULL if not blocking
28661 // obstacle found.
check_block_obstacle(entity * ent)28662 entity *check_block_obstacle(entity *ent)
28663 {
28664     entity *obstacle = NULL;
28665     int height;
28666 
28667     // Target entity affected by obstacles?
28668     if(ent->modeldata.subject_to_platform)
28669     {
28670         // Get the height. if entity does not have an
28671         // animation height defined, then use its
28672         // the entity height instead.
28673         if(ent->animation->size.y)
28674         {
28675             height = ent->animation->size.y;
28676         }
28677         else
28678         {
28679             height = ent->modeldata.size.y;
28680         }
28681 
28682         // Add Y position to get the exact Y axis
28683         // height of the entity's top edge.
28684         height += ent->position.y;
28685 
28686         // Find obstacle at entitiy's position (if any).
28687         obstacle = check_platform_between(ent->position.x, ent->position.z, ent->position.y, height, ent);
28688     }
28689 
28690     return obstacle;
28691 }
28692 
28693 // Caskey, Damon V
28694 // 2018-04-06
28695 //
28696 // Ricochet a projectile off of walls and platforms.
28697 // Returns 1 on successful ricochet. 0 otherwise.
projectile_wall_deflect(entity * ent)28698 int projectile_wall_deflect(entity *ent)
28699 {
28700     #define FALL_FORCE                  1000    // Knockdown force that will be applied to projectile entity.
28701     #define RICHOCHET_VELOCITY_X_FACTOR 0.25    // This value is multiplied by current velocity to get an X velocity value to bounce off wall..
28702     #define RICHOCHET_VELOCITY_Y        2.5     // Base Y velocity applied when projectile bounces off wall.
28703     #define RICHOCHET_VELOCITY_Y_RAND   1       // Random seed for Y variance added to base Y velocity when bouncing off wall.
28704 
28705     float richochet_velocity_x;
28706     s_collision_attack attack;
28707 
28708     if(validanim(ent, ANI_FALL))
28709     {
28710         int blocking_wall;
28711         entity *blocking_obstacle = NULL;
28712 
28713         blocking_wall = check_block_wall(self);
28714         blocking_obstacle = check_block_obstacle(self);
28715 
28716         if(blocking_wall >= 0 || blocking_obstacle) {
28717 
28718             // Use the projectiles speed and our factor to see how
28719             // hard it will bounce off wall.
28720             richochet_velocity_x = ent->velocity.x * RICHOCHET_VELOCITY_X_FACTOR;
28721 
28722             ent->takeaction = common_fall;
28723             ent->attacking = ATTACKING_NONE;
28724             ent->energy_state.health_current = 0;
28725             ent->projectile = BLAST_NONE;
28726             ent->velocity.x = (ent->direction == DIRECTION_RIGHT) ? (-richochet_velocity_x) : richochet_velocity_x;
28727             ent->damage_on_landing.attack_force = 0;
28728             ent->damage_on_landing.attack_type = ATK_NONE;
28729             toss(ent, RICHOCHET_VELOCITY_Y + randf(RICHOCHET_VELOCITY_Y_RAND));
28730 
28731             // Reset base detection
28732             ent->modeldata.subject_to_basemap = 1;
28733             ent->modeldata.no_adjust_base = 0;
28734             ent->modeldata.subject_to_hole = 1;
28735             ent->base = 0;
28736 
28737             // Use default attack values.
28738             attack = emptyattack;
28739             set_fall(ent, ent, &attack, 0);
28740 
28741             return 1;
28742         }
28743     }
28744 
28745     // Did not ricochet, so return false.
28746     return 0;
28747 
28748     #undef FALL_FORCE
28749     #undef RICHOCHET_VELOCITY_X_FACTOR
28750     #undef RICHOCHET_VELOCITY_Y
28751     #undef RICHOCHET_VELOCITY_Y_RAND
28752 }
28753 
28754 // Caskey, Damon V.
28755 // 2018-04-06
28756 //
28757 // Invert current sorting position vs. parent.
sort_invert_by_parent(entity * ent,entity * parent)28758 void sort_invert_by_parent(entity *ent, entity *parent)
28759 {
28760     if(ent->sortid <= parent->sortid)
28761     {
28762         ent->sortid = parent->sortid + 1;
28763     }
28764     else
28765     {
28766         ent->sortid = parent->sortid - 1;
28767     }
28768 }
28769 
28770 // for common bomb types
bomb_move()28771 int bomb_move()
28772 {
28773     if(inair(self) && self->toexplode & EXPLODE_PREPARED)
28774     {
28775         if(self->direction == DIRECTION_LEFT)
28776         {
28777             self->velocity.x = -self->modeldata.speed;
28778         }
28779         else if(self->direction == DIRECTION_RIGHT)
28780         {
28781             self->velocity.x = self->modeldata.speed;
28782         }
28783     }
28784     else if(self->takeaction != bomb_explode)
28785     {
28786         self->takeaction = bomb_explode;
28787 
28788         // hit something, just make it an explosion animation.
28789         self->modeldata.subject_to_wall = 1;
28790         self->modeldata.subject_to_platform = 1;
28791         self->modeldata.subject_to_hole = 1;
28792         //self->modeldata.no_adjust_base = 1;    // Stop moving up/down
28793         self->modeldata.subject_to_basemap = 1;
28794 
28795         if ( !checkhole(self->position.x, self->position.z) ) {
28796             self->velocity.y = 0;    // Stop moving up/down
28797             self->base = self->position.y;
28798             self->velocity.x = self->velocity.z = 0;
28799         }
28800 
28801         if(self->modeldata.diesound >= 0)
28802         {
28803             sound_play_sample(self->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100);
28804         }
28805 
28806         if(self->toexplode & EXPLODE_DETONATE && validanim(self, ANI_ATTACK2))
28807         {
28808             ent_set_anim(self, ANI_ATTACK2, 0);    // If bomb never reaces the ground, play this
28809         }
28810         else if (validanim(self, ANI_ATTACK1))
28811         {
28812             ent_set_anim(self, ANI_ATTACK1, 0);
28813         }
28814     }
28815     return 1;
28816 }
28817 
star_move()28818 int star_move()
28819 {
28820     if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80) || (self->position.y <= self->base && !self->modeldata.falldie))
28821     {
28822         kill_entity(self);
28823         return 0;
28824     }
28825 
28826     //self->base -= 4;
28827     //self->position.y = self->base;
28828 
28829     // Bounce off walls or platforms.
28830     projectile_wall_deflect(self);
28831 
28832     if(self->landed_on_platform || self->position.y <= self->base)
28833     {
28834         self->takeaction = common_lie;
28835         self->energy_state.health_current = 0;
28836         if(self->modeldata.nodieblink == 2)
28837         {
28838             self->animating = ANIMATING_NONE;
28839         }
28840     }
28841 
28842     return 1;
28843 }
28844 
28845 
28846 //dispatch move patterns
28847 //root function for aimove
common_move()28848 int common_move()
28849 {
28850     int aimove, makestop = 0, reachx, reachz;
28851     int air = inair(self);
28852     entity *other = NULL; //item
28853     entity *target = NULL;//hostile target
28854     entity *owner = NULL;
28855     entity *ent = NULL;
28856     int predir, stall;
28857     int patx[5], pxc, px, patz[5], pzc, pz, fz; //move pattern in z and x
28858 
28859     if(self->modeldata.aimove == -1)
28860     {
28861         return 0;    // invalid value
28862     }
28863 
28864     // get move pattern
28865     aimove = self->modeldata.aimove & MASK_AIMOVE1;
28866 
28867 //if(stricmp(self->name, "os")==0) printf("%d\n", aimove);
28868     // old and outdated patterns, but MUST be kept anyway
28869     if(aimove & AIMOVE1_BIKER)
28870     {
28871         // for biker subtype
28872         return biker_move();
28873     }
28874     else if(aimove & AIMOVE1_ARROW)
28875     {
28876         // for common straight-flying arrow
28877         return arrow_move();
28878     }
28879     else if(aimove & AIMOVE1_STAR)
28880     {
28881         // for a star, disappear when hit ground
28882         return star_move();
28883     }
28884     else if(aimove & AIMOVE1_BOMB)
28885     {
28886         // for a bomb, travel in a arc
28887         return bomb_move();
28888     }
28889     else if(aimove & AIMOVE1_NOMOVE)
28890     {
28891         // no move, just return
28892         return 0;
28893     }
28894     else
28895     {
28896         // all above are special move patterns, real AI goes here:
28897 
28898         // skip if the entity is in air,
28899         // removing this and entity might be spawned walking in air
28900         if(air)
28901         {
28902             return 0;
28903         }
28904 
28905         // store this for turning checking
28906         predir = self->direction;
28907 
28908         // find all possible entities for target
28909         // bad for optimization, but makes better sense
28910         if(self->custom_target == NULL || !self->custom_target->exists ) target = normal_find_target(-1, 0); // confirm the target again
28911         else target = self->custom_target;
28912 
28913         other = ( (_time / GAME_SPEED + self->energy_state.health_current / 3 + 1000) % 15 < 10) ? normal_find_item() : NULL; // find an item
28914         owner = self->parent;
28915 
28916         // temporary solution to turn off running if xdir is not set
28917         // unless one day vertical running logic is written
28918         if(!self->velocity.x)
28919         {
28920             self->running = 0;
28921         }
28922 
28923         // change direction unless the ai pattern ignores target or model has noflip
28924         if(!self->modeldata.noflip && !self->running && aimove != AIMOVE1_WANDER)
28925         {
28926             if(other)   //try to pick up an item, if possible
28927             {
28928                 self->direction = (self->position.x < other->position.x);
28929             }
28930             else if(target)
28931             {
28932                 self->direction = (self->position.x < target->position.x);
28933             }
28934             else if(owner)
28935             {
28936                 self->direction = (self->position.x < owner->position.x);
28937             }
28938         }
28939         else if(aimove == AIMOVE1_WANDER)
28940         {
28941             if(self->velocity.x)
28942             {
28943                 self->direction = (self->velocity.x > 0);
28944             }
28945         }
28946 
28947         //turn back if we have a turn animation
28948         // TODO, make a function for ai script
28949         if(self->direction != predir && validanim(self, ANI_TURN) && self->ducking == DUCK_NONE)
28950         {
28951             self->takeaction = common_turn;
28952             self->direction = !self->direction;
28953             self->velocity.x = self->velocity.z = 0;
28954             ent_set_anim(self, ANI_TURN, 0);
28955             return 1;
28956         }
28957 
28958         //pick up the item if possible
28959         if(other && diff(other->position.x, self->position.x) < (self->modeldata.grabdistance * 0.83333)
28960                 && diff(other->position.z, self->position.z) < (self->modeldata.grabdistance / 3) &&
28961                 other->animation->vulnerable[other->animpos])//won't pickup an item that is not previous one
28962         {
28963             if(diff(self->base, other->position.y) < 0.1)
28964             {
28965                 common_pickupitem(other);
28966                 return 1;
28967             }
28968         }
28969 
28970         if(self->modeldata.nomove)
28971         {
28972             self->idling = IDLING_PREPARED;
28973             return 1;
28974         }
28975 
28976         if(common_try_jump())
28977         {
28978             self->numwaypoints = 0;
28979             return 1;  //need to jump? so quit
28980         }
28981 
28982         if(checkpathblocked())
28983         {
28984             return 1;    // handle path blocked logic
28985         }
28986 
28987         // judge next move if stalltime is expired
28988         // skip if waypoints presents (passive move)
28989         if(self->stalltime < _time && !self->waypoints)
28990         {
28991             if(other)
28992             {
28993                 // try walking to the item
28994                 common_try_pick(other);
28995                 ent = other;
28996             }
28997             else
28998             {
28999                 if(target && (self->modeldata.subtype == SUBTYPE_CHASE ||
29000                               ((self->modeldata.type & TYPE_NPC) && self->parent)))
29001                     // try chase a target
29002                 {
29003                     aimove |= AIMOVE1_CHASE;
29004                 }
29005 
29006                 if(aimove & AIMOVE1_CHASE)
29007                 {
29008                     aimove |= AIMOVE1_CHASEX | AIMOVE1_CHASEZ;
29009                 }
29010                 if(aimove & AIMOVE1_AVOID)
29011                 {
29012                     aimove |= AIMOVE1_AVOIDX | AIMOVE1_AVOIDZ;
29013                 }
29014 
29015                 if(other != ent)
29016                 {
29017                     self->velocity.x = self->velocity.z = 0;
29018                 }
29019 
29020                 if(!aimove && target)
29021                 {
29022                     common_try_wander(target, 1, 1);
29023                     ent = target;
29024                 }
29025                 else if ( target && (!(self->modeldata.aimove&AIMOVE2_NOTARGETIDLE) || ((self->modeldata.aimove&AIMOVE2_NOTARGETIDLE) && target->animnum != ANI_IDLE)) )
29026                 {
29027                     ent = target;
29028                     pxc = pzc = 0;
29029 
29030                     if(aimove & AIMOVE1_WANDER)
29031                     {
29032                         patx[pxc++] = AIMOVE1_WANDER;
29033                         patx[pxc++] = AIMOVE1_WANDER;
29034                         patz[pzc++] = AIMOVE1_WANDER;
29035                         patz[pzc++] = AIMOVE1_WANDER;
29036                     }
29037                     if(aimove & AIMOVE1_CHASEX)
29038                     {
29039                         patx[pxc++] = AIMOVE1_CHASEX;
29040                     }
29041                     if(aimove & AIMOVE1_AVOIDX)
29042                     {
29043                         patx[pxc++] = AIMOVE1_AVOIDX;
29044                     }
29045                     if(aimove & AIMOVE1_CHASEZ)
29046                     {
29047                         patz[pzc++] = AIMOVE1_CHASEZ;
29048                     }
29049                     if(aimove & AIMOVE1_AVOIDZ)
29050                     {
29051                         patz[pzc++] = AIMOVE1_AVOIDZ;
29052                     }
29053                     if(!pxc)
29054                     {
29055                         patx[pxc++] = AIMOVE1_WANDER;
29056                     }
29057                     if(!pzc)
29058                     {
29059                         patz[pzc++] = AIMOVE1_WANDER;
29060                     }
29061                     px = patx[(rand32() & 0xff) % pxc];
29062                     pz = patz[(rand32() & 0xff) % pzc];
29063 
29064                     fz = 0;
29065 
29066                     aimove = (self->modeldata.aimove & MASK_AIMOVE1);
29067 
29068                     //valid types: avoidx, aviodz, chasex, chasez, wander
29069                     if(px == AIMOVE1_WANDER)
29070                     {
29071                         if (pz == AIMOVE1_WANDER && aimove == AIMOVE1_WANDER)
29072                         {
29073                             common_try_wandercompletely(1, 1);
29074                             fz = 1;
29075                         }
29076                         else
29077                         {
29078                             common_try_wander(target, 1, 0);
29079                         }
29080 
29081                     }
29082                     else if(px == AIMOVE1_CHASEX)
29083                     {
29084                         common_try_chase(target, 1, (pz == AIMOVE1_CHASEZ));
29085                         fz = (pz == AIMOVE1_CHASEZ);
29086                     }
29087                     else if (px == AIMOVE1_AVOIDX)
29088                     {
29089                         common_try_avoid(target, 1, (pz == AIMOVE1_AVOIDZ));
29090                         fz = (pz == AIMOVE1_AVOIDZ);
29091                     }
29092                     if(!fz)
29093                     {
29094                         if(pz == AIMOVE1_WANDER)
29095                         {
29096                             common_try_wander(target, 0, 1);
29097                         }
29098                         else if(pz == AIMOVE1_CHASEZ)
29099                         {
29100                             common_try_chase(target, 0, 1);
29101                         }
29102                         else if (pz == AIMOVE1_AVOIDZ)
29103                         {
29104                             common_try_avoid(target, 0, 1);
29105                         }
29106                     }
29107 
29108                 }
29109                 else if(!common_try_follow(owner, 1, 1) && !(self->modeldata.aimove & AIMOVE2_NOTARGETIDLE) )
29110                 {
29111                     common_try_wandercompletely(1, 1);
29112                     ent = NULL;
29113                 }
29114                 else
29115                 {
29116                     ent = owner;
29117                 }
29118             }
29119             //end of if
29120 
29121         }//if(self->stalltime < _time )
29122         else
29123         {
29124             ent = other;
29125             if(!ent)
29126             {
29127                 ent = target;
29128             }
29129             if(!ent)
29130             {
29131                 ent = owner;
29132             }
29133         }
29134         if(self->numwaypoints == 0 && self->waypoints)
29135         {
29136             free(self->waypoints);
29137             self->waypoints = NULL;
29138         }
29139 
29140         //fix 2d level panic, or should this be moved to the very beginning?
29141         if(self->modeldata.subject_to_minz > 0 && self->destz < PLAYER_MIN_Z)
29142         {
29143             self->destz = PLAYER_MIN_Z;
29144         }
29145         if(self->modeldata.subject_to_maxz > 0 && self->destz > PLAYER_MAX_Z)
29146         {
29147             self->destz = PLAYER_MAX_Z;
29148         }
29149 
29150         // don't run in passive move mode. The path could be complex and running may look bad.
29151         if(self->waypoints)
29152         {
29153             self->running = 0;
29154         }
29155 
29156         if(self->direction == (self->destx < self->position.x))
29157         {
29158             self->running = 0;
29159         }
29160 
29161         // make the entity walks in a straight path instead of flickering here and there
29162         // acceleration can be added easily based on this logic, if necessary
29163         adjustspeed(self->running ? self->modeldata.runspeed : self->modeldata.speed,
29164                                                     self->position.x, self->position.z,
29165                                                     self->destx, self->destz,
29166                                                     &self->velocity.x,
29167                                                     &self->velocity.z);
29168 
29169         // fix running animation, if the model doesn't allow running updown then set zdir to 0
29170         if(self->running && !self->modeldata.runupdown)
29171         {
29172             self->velocity.z = 0;
29173             self->destz = self->position.z;
29174         }
29175 
29176         // check destination point to make a stop or pop a waypoint from the stack
29177         reachx = (diff(self->position.x, self->destx) < MAX(1, ABS(self->velocity.x)));
29178         reachz = (diff(self->position.z, self->destz) < MAX(1, ABS(self->velocity.z)));
29179 
29180         // check destination point to make a stop or pop a waypoint from the stack
29181         if(reachx && reachz)
29182         {
29183             if(self->waypoints && self->numwaypoints)
29184             {
29185                 self->destx = self->waypoints[self->numwaypoints - 1].x;
29186                 self->destz = self->waypoints[self->numwaypoints - 1].z;
29187                 self->numwaypoints--;
29188             }
29189             else if(self->velocity.x || self->velocity.z)
29190             {
29191                 makestop = 1;
29192             }
29193         }
29194 
29195         if(!self->waypoints || !self->numwaypoints)
29196         {
29197             if(reachx)
29198             {
29199                 self->velocity.x = 0;
29200                 self->destx = self->position.x;
29201             }
29202             if(reachz)
29203             {
29204                 self->velocity.z = 0;
29205                 self->destz = self->position.z;
29206             }
29207         }
29208 
29209         // IMPORTANT: stoped so play idle, preventinng funny stepping bug, but may cause flickering
29210         if(!self->velocity.x && !self->velocity.z && !self->waypoints)
29211         {
29212             set_idle(self);
29213             if(makestop)
29214             {
29215                 stall = (GAME_SPEED - self->modeldata.aggression) / 2;
29216                 if(stall < GAME_SPEED / 5)
29217                 {
29218                     stall = GAME_SPEED / 5;
29219                 }
29220                 self->stalltime = _time + MAX(0, stall);
29221             }
29222         }
29223         else
29224         {
29225             // readjust walk animation
29226             adjust_walk_animation(ent);
29227             // give proper stalltime if destination point is not reached
29228             // if the destination point is not reachable,
29229             // it should be already handled in checkpathblocked
29230             if(_time > self->stalltime)
29231             {
29232                 if(ABS(self->velocity.x) > ABS(self->velocity.z))
29233                 {
29234                     stall = diff(self->destx, self->position.x) / ABS(self->velocity.x) * 2;
29235                 }
29236                 else if(self->velocity.z)
29237                 {
29238                     stall = diff(self->destz, self->position.z) / ABS(self->velocity.z) * 2;
29239                 }
29240                 else
29241                 {
29242                     stall = GAME_SPEED / 2;
29243                 }
29244                 self->stalltime = _time + MAX(0, stall);
29245             }
29246         }
29247 
29248         //target is moving?  readjust destination sooner
29249         if(aimove != AIMOVE1_WANDER && !self->waypoints && ent && (self->velocity.x || self->velocity.z) && (ent->velocity.x || ent->velocity.z))
29250         {
29251             if(self->running && self->stalltime > _time + GAME_SPEED / 2)
29252             {
29253                 self->stalltime = _time + GAME_SPEED / 2;
29254             }
29255             else if(!self->running && self->stalltime > _time + GAME_SPEED / 5)
29256             {
29257                 self->stalltime = _time + GAME_SPEED / 5;
29258             }
29259         }
29260 
29261         return 1;
29262 
29263     }
29264 
29265     return 1;
29266 }
29267 
29268 
decide_stalker()29269 void decide_stalker()
29270 {
29271     entity *ent, *furthest = NULL;
29272     int i;
29273     int l = 0, r = 0;
29274     float maxz = 0.0f, z;
29275 
29276     if(stalker && stalking)
29277     {
29278         return;
29279     }
29280 
29281     firstplayer = NULL;
29282 
29283     for(i = 0; i < MAX_PLAYERS; i++)
29284     {
29285         if(player[i].ent)
29286         {
29287             firstplayer = player[i].ent;
29288             break;
29289         }
29290     }
29291 
29292     if(!firstplayer)
29293     {
29294         return;
29295     }
29296 
29297     for(i = 0; i < ent_max; i++)
29298     {
29299         ent = ent_list[i];
29300 
29301         if(ent->exists && !ent->dead && (ent->modeldata.type & TYPE_ENEMY))
29302         {
29303             if(ent->position.x > firstplayer->position.x)
29304             {
29305                 r++;
29306             }
29307             else
29308             {
29309                 l++;
29310             }
29311 
29312             if((z = diff(ent->position.z, firstplayer->position.z)) >= maxz &&
29313                     (ent->modeldata.aimove == 0 || (ent->modeldata.aimove & AIMOVE1_CHASE))) // 2 mostly used type
29314             {
29315                 maxz = z;
29316                 furthest = ent;
29317             }
29318         }
29319     }
29320 
29321     if((l > 1 && !r) || (r > 1 && !l))
29322     {
29323         stalker = furthest;
29324         //printf("** stalker decided: %s @ _time %d\n", stalker->name, _time);
29325     }
29326 }
29327 
29328 
plan()29329 void plan()
29330 {
29331     decide_stalker();
29332 }
29333 
checkstalker()29334 void checkstalker()
29335 {
29336     float maxspeed;
29337     int running;
29338 
29339     if(self != stalker)
29340     {
29341         return;
29342     }
29343 
29344     if(!firstplayer)
29345     {
29346         stalker = NULL;
29347         return;
29348     }
29349 
29350     if(stalking)
29351     {
29352         if(self->stalltime <= _time)
29353         {
29354             //printf("** stalk _time expired: %s @ _time %d\n", stalker->name, _time);
29355             stalker = NULL;
29356         }
29357         return;
29358     }
29359 
29360     running = validanim(self, ANI_RUN);
29361 
29362     maxspeed = running ? self->modeldata.runspeed : self->modeldata.speed;
29363 
29364     self->velocity.x = maxspeed;
29365     self->velocity.z = 0;
29366 
29367     if(self->position.x > firstplayer->position.x)
29368     {
29369         self->velocity.x = -self->velocity.x;
29370     }
29371 
29372     self->running = running;
29373 
29374 
29375     self->stalltime = _time + (diff(self->position.x, firstplayer->position.x) + 150) / maxspeed * THINK_SPEED;
29376 
29377     adjust_walk_animation(firstplayer);
29378 
29379     stalking = 1;
29380     //printf("**start stalking: %s @ _time %d till @%d\n", stalker->name, time, self->stalltime);
29381 }
29382 
checkplanned()29383 int checkplanned()
29384 {
29385     return 0;
29386 }
29387 
29388 
ai_check_warp()29389 int ai_check_warp()
29390 {
29391     if(self->link)
29392     {
29393         return 0;
29394     }
29395 
29396     if(self->modeldata.subtype == SUBTYPE_FOLLOW && self->parent && validanim(self, ANI_IDLE) &&
29397             (diff(self->position.z, self->parent->position.z) > self->modeldata.animation[ANI_IDLE]->range.x.max ||
29398              diff(self->position.x, self->parent->position.x) > self->modeldata.animation[ANI_IDLE]->range.x.max) )
29399     {
29400         self->takeaction = npc_warp;
29401         return 1;
29402     }
29403     return 0;
29404 }
29405 
ai_check_lie()29406 int ai_check_lie()
29407 {
29408     if(self->drop
29409 		&& self->position.y == self->base
29410 		&& !self->velocity.y
29411 		&& validanim(self, ANI_RISEATTACK)
29412 		&& ((rand32() % (self->stalltime - _time + 1)) < 3)
29413 		&& (self->energy_state.health_current > 0
29414 		&& _time > self->staydown.riseattack_stall))
29415     {
29416         common_try_riseattack();
29417         return 1;
29418     }
29419     return 0;
29420 }
29421 
ai_check_grabbed()29422 int ai_check_grabbed()
29423 {
29424     if(self->link && !self->grabbing && !self->inpain && self->takeaction != common_prethrow && !inair(self) &&
29425             _time >= self->stalltime && validanim(self, ANI_SPECIAL))
29426     {
29427         check_special();
29428         return 1;
29429     }
29430     return 0;
29431 }
29432 
ai_check_grab()29433 int ai_check_grab()
29434 {
29435     if(self->grabbing && self->attacking == ATTACKING_NONE)
29436     {
29437         common_grab_check();
29438         return 1;
29439     }
29440     return 0;
29441 }
29442 
ai_check_escape()29443 int ai_check_escape()
29444 {
29445     if((self->escapecount > self->modeldata.escapehits) && !inair(self) && validanim(self, ANI_SPECIAL2))
29446     {
29447         // Counter the player!
29448         check_costmove(ANI_SPECIAL2, 0, 0);
29449         return 1;
29450     }
29451     return 0;
29452 }
29453 
ai_check_busy()29454 int ai_check_busy()
29455 {
29456     return self->link || !self->idling;
29457 }
29458 
ai_check_ducking()29459 int ai_check_ducking()
29460 {
29461     int them;
29462     entity *target = NULL;
29463     float t_rangex = 60.0f;
29464     float t_rangez = 30.0f;
29465 
29466     if ((self->ducking & DUCK_PREPARED) || (self->ducking & DUCK_RISE))
29467     {
29468         return 1;
29469     }
29470 
29471     if(self->link || inair(self))
29472     {
29473         return 0;
29474     }
29475 
29476     if( !validanim(self, ANI_DUCK) || !validanim(self, ANI_DUCKATTACK) )
29477     {
29478         return 0;
29479     }
29480 
29481     if(self->projectile & BLAST_ATTACK)
29482     {
29483         them = self->modeldata.projectilehit;
29484     }
29485     else
29486     {
29487         them = self->modeldata.candamage;
29488     }
29489 
29490     if(self->custom_target == NULL || !self->custom_target->exists ) target = normal_find_target(-1, 0);
29491     else target = self->custom_target;
29492 
29493     if(target && !(target->modeldata.type & them))
29494     {
29495         return 0;
29496     }
29497 
29498     if ((self->ducking & DUCK_ACTIVE) && self->animnum == ANI_DUCK)
29499     {
29500         if ( !target || inair(target) || !(target->ducking & DUCK_ACTIVE) ||
29501              (!check_range_target_all(self,target,ANI_DUCK) &&
29502                 diff(self->position.x, target->position.x) > t_rangex && diff(self->position.z, target->position.z) > t_rangez) )
29503         {
29504             self->velocity.x = self->velocity.z = 0;
29505             tryduckrise(self);
29506             return 1;
29507         }
29508     }
29509 
29510     if(!target || !(target->ducking & DUCK_ACTIVE))
29511     {
29512         return 0;
29513     }
29514     else
29515     {
29516         if (self->ducking & DUCK_ACTIVE)
29517         {
29518             return 1;
29519         }
29520         else
29521         {
29522             int range_flag = check_range_target_all(self,target,ANI_DUCK);
29523             if (!range_flag)
29524             {
29525                 if ( diff(self->position.x, target->position.x) <= t_rangex &&
29526                      diff(self->position.z, target->position.z) <= t_rangez) range_flag = 1;
29527             }
29528             if (!range_flag) return 0;
29529 
29530             if(self->ducking == DUCK_NONE)
29531             {
29532                 tryduck(self);
29533             }
29534             return 1;
29535         }
29536     }
29537 
29538     return 0;
29539 }
29540 
29541 
29542 // A.I root
common_think()29543 void common_think()
29544 {
29545 
29546     if(self->dead)
29547     {
29548         return;
29549     }
29550 
29551     //if(checkplanned()) return;
29552 
29553     // too far away , do a warp
29554     if(ai_check_warp())
29555     {
29556         return;
29557     }
29558 
29559     // rise? try rise attack
29560     if(ai_check_lie())
29561     {
29562         return;
29563     }
29564 
29565     // Escape?
29566     if(ai_check_grabbed())
29567     {
29568         return;
29569     }
29570 
29571     //grabbing something
29572     if(ai_check_grab())
29573     {
29574         return;
29575     }
29576 
29577     // Enemies can now escape non-knockdown spammage (What a weird phrase)!
29578     if(ai_check_escape())
29579     {
29580         return;
29581     }
29582 
29583     // busy right now?
29584     if(ai_check_busy())
29585     {
29586         return;
29587     }
29588 
29589     // idle, so try to attack or judge next move
29590     // dont move if fall into a hole or off a wall
29591     if(common_attack())
29592     {
29593         return;
29594     }
29595 
29596     // target is ducking? try to ducking..
29597     if(ai_check_ducking())
29598     {
29599         return;
29600     }
29601 
29602     common_move();
29603 }
29604 
29605 //////////////////////////////////////////////////////////////////////////
29606 
suicide()29607 void suicide()
29608 {
29609     if(_time < self->stalltime)
29610     {
29611         return;
29612     }
29613     level_completed |= self->boss;
29614     level_completed_defeating_boss |= self->boss;
29615     kill_entity(self);
29616 }
29617 
29618 
29619 
29620 // Re-enter playfield
29621 // Used by player_fall and player_takedamage
player_die()29622 void player_die()
29623 {
29624     int playerindex = self->playerindex;
29625     int i = 0;
29626 
29627     if(!livescheat)
29628     {
29629         --player[playerindex].lives;
29630     }
29631 
29632     if(firstplayer == self)
29633     {
29634         firstplayer = NULL;
29635     }
29636 
29637     execute_pdie_script(playerindex);
29638 
29639     if(nomaxrushreset[4] >= 1)
29640     {
29641         nomaxrushreset[playerindex] = player[playerindex].ent->rush.count.max;
29642     }
29643     player[playerindex].ent = NULL;
29644     player[playerindex].spawnhealth = self->modeldata.health;
29645     player[playerindex].spawnmp = self->modeldata.mp;
29646 
29647     if(self->modeldata.nodieblink != 3)
29648     {
29649         kill_entity(self);
29650     }
29651     else
29652     {
29653         self->think = NULL;
29654         self->takeaction = NULL;
29655         self->modeldata.type = TYPE_NONE;
29656     }
29657 
29658     if(player[playerindex].lives <= 0)
29659     {
29660         int all_p_ko = 0;
29661 
29662 		// Count number of KO'd (dead) players, by looping player
29663 		// indexes and incrementing when player does not have
29664 		// an entity.
29665         for(i = 0; i < MAX_PLAYERS; i++)
29666         {
29667 			if (!player[i].ent)
29668 			{
29669 				++all_p_ko;
29670 			}
29671         }
29672 
29673 		// If all players are KO'd, then KO count = 1.
29674 		// Otherwise, set it to 0.
29675 		all_p_ko = (all_p_ko >= MAX_PLAYERS) ? 1 : 0;
29676 
29677 		// All players KO'd?
29678         if(all_p_ko)
29679         {
29680 			int all_p_nojoin = 0;
29681 			int all_p_nocredits = 0;
29682 
29683 			// All players NOT joining?
29684 			// Same logic as all player KO.
29685             for(i = 0; i < MAX_PLAYERS; i++)
29686             {
29687 				if (!player[i].joining)
29688 				{
29689 					++all_p_nojoin;
29690 				}
29691             }
29692 
29693             all_p_nojoin = (all_p_nojoin >= MAX_PLAYERS) ? 1 : 0;
29694 
29695 			// All players out of credits?
29696 			// Same logic as all player KO.
29697             for(i = 0; i < MAX_PLAYERS; i++)
29698             {
29699 				if (player[i].credits < 1)
29700 				{
29701 					++all_p_nocredits;
29702 				}
29703             }
29704 
29705             all_p_nocredits = (all_p_nocredits >= MAX_PLAYERS) ? 1 : 0;
29706 
29707 			// Set the timer to a 10 second count down.
29708             timeleft = 10 * COUNTER_SPEED;
29709 
29710 			// No one joining in?
29711             if(all_p_nojoin)
29712             {
29713 				// If the player can't continue, let's set the time over
29714 				// to end almost instantly so they won't have to wait.
29715 
29716 				// If noshare is enabled, credit shares are not allowed. Verify all
29717 				// player individual credit supplies are empty. Otherwise credit
29718 				// shares are allowed, so verify pool of credits is empty.
29719 				if (noshare)
29720 				{
29721 					if (all_p_nocredits)
29722 					{
29723 						timeleft = COUNTER_SPEED / 2;
29724 					}
29725 				}
29726 				else
29727 				{
29728 					if (credits < 1)
29729 					{
29730 						timeleft = COUNTER_SPEED / 2;
29731 					}
29732 				}
29733             }
29734         }
29735 
29736         if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_CHANGE)
29737         {
29738             player[playerindex].weapnum = level->setweap;
29739         }
29740 
29741         if(nomaxrushreset[4] != 2)
29742         {
29743             nomaxrushreset[playerindex] = 0;
29744         }
29745 
29746 		return;
29747     }
29748     else
29749     {
29750         spawnplayer(playerindex);
29751         execute_respawn_script(playerindex);
29752         if(!nodropen)
29753         {
29754             if (savedata.joyrumble[playerindex]) control_rumble(playerindex, 1, 125);
29755             drop_all_enemies();
29756         }
29757     }
29758 
29759     if(!level->noreset)
29760     {
29761         timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
29762     }
29763 
29764 }
29765 
29766 
29767 
player_trymove(float xdir,float zdir)29768 int player_trymove(float xdir, float zdir)
29769 {
29770     return common_trymove(xdir, zdir);
29771 }
29772 
check_energy(e_cost_check which,int ani)29773 int check_energy(e_cost_check which, int ani)
29774 {
29775     int result = FALSE;
29776     e_entity_type type;        //Entity type.
29777     s_energycost energycost;
29778 
29779     energycost.cost     = 0;
29780     energycost.disable  = 0;
29781     energycost.mponly   = COST_TYPE_MP_THEN_HP;
29782 
29783     // Get animation's energycost if available.
29784     if(self->modeldata.animation[ani]->energycost)
29785     {
29786         energycost = *self->modeldata.animation[ani]->energycost;
29787     }
29788 
29789     // Get entity type.
29790     type	   = self->modeldata.type;
29791 
29792     // If we're bind and special is overridden, then
29793     // return false.
29794     if(type & (TYPE_ENEMY  | TYPE_NPC))
29795     {
29796         if(check_bind_override(self, BIND_OVERRIDE_SPECIAL_AI))
29797         {
29798             return FALSE;
29799         }
29800     }
29801     else if(type & TYPE_PLAYER)
29802     {
29803         if(check_bind_override(self, BIND_OVERRIDE_SPECIAL_PLAYER))
29804         {
29805             return FALSE;
29806         }
29807     }
29808 
29809     if(self->modeldata.animation[ani])
29810     {
29811         // Caskey, Damon V.
29812         // 2010-05-08
29813         //
29814         // It is now possible to individually disable specials. In
29815         // many cases (weapons in particular) this can	help cut down the need for
29816         // superfluous models when differing abilities are desired for players,
29817         // enemies, or npcs.
29818         if(!(energycost.disable == type													// Disabled by type?
29819                 || (energycost.disable == -1)											    // Disabled for all?
29820                 || (energycost.disable == -2 && (type & (TYPE_ENEMY  | TYPE_NPC)))		    // Disabled for all AI?
29821                 || (energycost.disable == -3 && (type & (TYPE_PLAYER | TYPE_NPC)))	        // Disabled for players & NPCs?
29822                 || (energycost.disable == -4 && (type & (TYPE_PLAYER | TYPE_ENEMY)))))     // Disabled for all AI?
29823         {
29824             // No seal or seal is less/same as energy cost?
29825             if(!self->seal || self->seal >= energycost.cost)
29826             {
29827                 if(validanim(self, ani) &&										    //Validate the animation one more time.
29828                         ((which == COST_CHECK_MP &&			                    //Check magic validity
29829                           (energycost.mponly != COST_TYPE_HP_ONLY) &&
29830                           (self->energy_state.mp_current >= energycost.cost)) ||
29831                          (which == COST_CHECK_HP &&			                    //Check health validity
29832                           (energycost.mponly != COST_TYPE_MP_ONLY) &&
29833                           (self->energy_state.health_current > energycost.cost))))
29834                 {
29835                     result = TRUE;
29836                 }
29837                 else
29838                 {
29839                     //DC 2009-01-23
29840                     //Tried putting the CANT animation here to keep code compacted, but won't work. I'll come back to this.
29841                     //if (validanim(self,ani)){
29842                     //    ent_set_anim(self, ANI_CANT, 0);
29843                     //    self->takeaction = common_attack_proc;
29844                     //    player[self->playerindex].playkeys = 0;
29845                     //}
29846                 }
29847             }
29848         }
29849     }
29850 
29851     return result;
29852 }
29853 
29854 // Caskey Damon V.
29855 // 2018-05-10
29856 //
29857 // Replaces unreadable check_range() macro. Runs individual
29858 // check range functions for each axis and returns true
29859 // if target is within range of ALL.
check_range_target_all(entity * ent,entity * target,e_animations animation_id)29860 int check_range_target_all(entity *ent, entity *target, e_animations animation_id)
29861 {
29862     s_anim *animation; // Current animation
29863 
29864     // Must have a valid target entity.
29865     if(!target)
29866     {
29867         return 0;
29868     }
29869 
29870     // Get pointer to animation.
29871     animation = ent->modeldata.animation[animation_id];
29872 
29873     // Return result of individual axis range checks.
29874     return(check_range_target_base(ent, target, animation)
29875            && check_range_target_x(ent, target, animation)
29876            && check_range_target_y(ent, target, animation)
29877            && check_range_target_z(ent, target, animation));
29878 }
29879 
29880 // Caskey, Damon V.
29881 // 2018-05-12
29882 //
29883 // Return true if target is within Base range
29884 // of entity's animation.
check_range_target_base(entity * ent,entity * target,s_anim * animation)29885 int check_range_target_base(entity *ent, entity *target, s_anim *animation)
29886 {
29887     int ent_base;
29888     int target_base;
29889 
29890     // Must have a target.
29891     if(!target)
29892     {
29893         return 0;
29894     }
29895 
29896     // Get positions cast as integers.
29897     ent_base       = (int)ent->base;
29898     target_base    = (int)target->base;
29899 
29900     // Subtract entity Base position from target position.
29901     target_base -= ent_base;
29902 
29903     // Return true if final target location is
29904     // within range min and max.
29905     return (target_base >= animation->range.base.min
29906             && target_base <= animation->range.base.max);
29907 }
29908 
29909 // Caskey, Damon V.
29910 // 2018-05-10
29911 //
29912 // Return true if target is within X range
29913 // of entity's animation.
check_range_target_x(entity * ent,entity * target,s_anim * animation)29914 int check_range_target_x(entity *ent, entity *target, s_anim *animation)
29915 {
29916     int ent_x;
29917     int target_x;
29918     s_metric_range range;
29919 
29920     // Must have a target.
29921     if(!target)
29922     {
29923         return 0;
29924     }
29925 
29926     // Get positions cast as integers.
29927     ent_x       = (int)ent->position.x;
29928     target_x    = (int)target->position.x;
29929 
29930     // Return true if final target location is
29931     // within range X min and max. Range comparison
29932     // is reversed when entity faces left.
29933     if(ent->direction == DIRECTION_RIGHT)
29934     {
29935         // Add animation range to entity X position
29936         // for final X range coordinates.
29937         range.min = ent_x + animation->range.x.min;
29938         range.max = ent_x + animation->range.x.max;
29939 
29940         return (target_x >= range.min
29941                 && target_x <= range.max);
29942     }
29943     else
29944     {
29945         // Subtract animation range from entity X
29946         // position for final X range coordinates.
29947         range.min = ent_x - animation->range.x.min;
29948         range.max = ent_x - animation->range.x.max;
29949 
29950         return (target_x <= range.min
29951                 && target_x >= range.max);
29952     }
29953 }
29954 
29955 // Caskey, Damon V.
29956 // 2018-05-10
29957 //
29958 // Return true if target is within Y range
29959 // of entity's animation.
check_range_target_y(entity * ent,entity * target,s_anim * animation)29960 int check_range_target_y(entity *ent, entity *target, s_anim *animation)
29961 {
29962     int ent_y;
29963     int target_y;
29964 
29965     // Must have a target.
29966     if(!target)
29967     {
29968         return 0;
29969     }
29970 
29971     // Get positions cast as integers.
29972     ent_y       = (int)ent->position.y;
29973     target_y    = (int)target->position.y;
29974 
29975     // Subtract entity Y position from target position.
29976     target_y -= ent_y;
29977 
29978     // Return true if final target location is
29979     // within range min and max.
29980     return (target_y >= animation->range.y.min
29981             && target_y <= animation->range.y.max);
29982 }
29983 
29984 // Caskey, Damon V.
29985 // 2018-05-12
29986 //
29987 // Return true if target is within Z range
29988 // of entity's animation.
check_range_target_z(entity * ent,entity * target,s_anim * animation)29989 int check_range_target_z(entity *ent, entity *target, s_anim *animation)
29990 {
29991     int ent_z;
29992     int target_z;
29993 
29994     // Must have a target.
29995     if(!target)
29996     {
29997         return 0;
29998     }
29999 
30000     // Get positions cast as integers.
30001     ent_z       = (int)ent->position.z;
30002     target_z    = (int)target->position.z;
30003 
30004     // Subtract entity Z position from target position.
30005     target_z -= ent_z;
30006 
30007     // Return true if final target location is
30008     // within range min and max.
30009     return (target_z >= animation->range.z.min
30010             && target_z <= animation->range.z.max);
30011 }
30012 
30013 
30014 
check_special()30015 int check_special()
30016 {
30017     entity *e;
30018     if((!level->nospecial || level->nospecial == 3) && validanim(self, ANI_SPECIAL) &&
30019             (check_energy(COST_CHECK_HP, ANI_SPECIAL) || check_energy(COST_CHECK_MP, ANI_SPECIAL))
30020        )
30021     {
30022         self->takeaction = common_attack_proc;
30023         set_attacking(self);
30024         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
30025 
30026         e = self->link;
30027         if(e)
30028         {
30029             e->takeaction = NULL;
30030             ent_unlink(self);
30031             set_idle(e);
30032         }
30033 
30034         if(self->modeldata.smartbomb && !self->modeldata.dofreeze)
30035         {
30036             smart_bomb(self, self->modeldata.smartbomb); // do smartbomb immediately if it doesn't freeze screen
30037         }
30038 
30039         self->running = 0;    // If special is executed while running, ceases to run
30040         self->velocity.x = self->velocity.z = 0;
30041         ent_set_anim(self, ANI_SPECIAL, 0);
30042 
30043         if(self->modeldata.dofreeze)
30044         {
30045             smartbomber = self;    // Freezes the animations of all enemies/players while special is executed
30046         }
30047 
30048         if(!nocost && !healthcheat)
30049         {
30050             // Energycost defined?
30051             if(self->modeldata.animation[ANI_SPECIAL]->energycost)
30052             {
30053                 if(check_energy(COST_CHECK_MP, ANI_SPECIAL))
30054                 {
30055                     self->energy_state.mp_current -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
30056                 }
30057                 else
30058                 {
30059                     self->energy_state.health_current -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
30060                 }
30061             }
30062         }
30063 
30064         return 1;
30065     }
30066     return 0;
30067 }
30068 
30069 
30070 // Check keys for special move. Used several times, so I func'd it.
player_check_special()30071 int player_check_special()
30072 {
30073     u64 thekey = 0;
30074     if((!ajspecial || (ajspecial && !validanim(self, ANI_BLOCK))) &&
30075             (player[self->playerindex].playkeys & FLAG_SPECIAL))
30076     {
30077         thekey = FLAG_SPECIAL;
30078     }
30079     else if(ajspecial && ((player[self->playerindex].playkeys & FLAG_JUMP) &&
30080                           (player[self->playerindex].keys & FLAG_ATTACK)))
30081     {
30082         thekey = FLAG_JUMP;
30083     }
30084     else
30085     {
30086         return 0;
30087     }
30088 
30089     if(check_special())
30090     {
30091         self->stalltime = 0;
30092         player[self->playerindex].playkeys &= ~thekey;
30093         return 1;
30094     }
30095     else
30096     {
30097         return 0;
30098     }
30099 }
30100 
30101 
common_land()30102 void common_land()
30103 {
30104     self->velocity.x = self->velocity.z = 0;
30105     if(self->animating)
30106     {
30107         return;
30108     }
30109 
30110     self->takeaction = NULL;
30111     set_idle(self);
30112 }
30113 
30114 
30115 //animal run when you lost it 3 times by tails
runanimal()30116 void runanimal()
30117 {
30118     common_walk_anim(self);
30119     //ent_set_anim(self, ANI_WALK, 0);
30120 
30121     if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80))
30122     {
30123         kill_entity(self);
30124         return;
30125     }
30126 
30127     if(self->direction == DIRECTION_RIGHT)
30128     {
30129         self->position.x += self->modeldata.speed;
30130     }
30131     else
30132     {
30133         self->position.x -= self->modeldata.speed;
30134     }
30135 }
30136 
30137 
player_blink()30138 void player_blink()
30139 {
30140     self->blink = 1;
30141     if(_time >= self->stalltime)
30142     {
30143         player_die();
30144     }
30145 }
30146 
30147 
common_grabattack()30148 void common_grabattack()
30149 {
30150     if(self->animating)
30151     {
30152         return;
30153     }
30154 
30155     self->attacking = ATTACKING_NONE;
30156 
30157     if(!(self->combostep[0] || self->combostep[1] ||
30158             self->combostep[2] || self->combostep[3] ||
30159             self->combostep[4]))
30160     {
30161         ent_unlink(self);
30162     }
30163 
30164     if(self->link)
30165     {
30166         self->takeaction = common_grab;
30167         self->link->takeaction = common_grabbed;
30168         self->attacking = ATTACKING_NONE;
30169         ent_set_anim(self, ANI_GRAB, 0);
30170         set_pain(self->link, -1, 0);
30171         update_frame(self, self->animation->numframes - 1);
30172         update_frame(self->link, self->link->animation->numframes - 1);
30173     }
30174     else
30175     {
30176         self->takeaction = NULL;
30177         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
30178         set_idle(self);
30179     }
30180 }
30181 
30182 // Function that causes the player to continue to move up or down until the animation has finished playing
common_dodge()30183 void common_dodge()    // New function so players can dodge with up up or down down
30184 {
30185     if(self->animating)    // Continues to move as long as the player is animating
30186     {
30187         return;
30188     }
30189     else    // Once done animating, returns to thinking
30190     {
30191         self->takeaction = NULL;
30192         self->velocity.x = self->velocity.z = 0;
30193         set_idle(self);
30194     }
30195 }
30196 
30197 
common_preduck()30198 void common_preduck()
30199 {
30200     if(self->animating)
30201     {
30202         return;
30203     }
30204     doduck(self);
30205 }
30206 
30207 
common_idle()30208 void common_idle()
30209 {
30210     if(self->animating)
30211     {
30212         return;
30213     }
30214     self->takeaction = NULL;
30215     self->attacking = ATTACKING_NONE;
30216     self->idling = IDLING_PREPARED;
30217     common_idle_anim(self);
30218 }
30219 
30220 
tryduck(entity * ent)30221 void tryduck(entity *ent)
30222 {
30223     ent->running = 0;
30224     if(validanim(ent, ANI_DUCKING))
30225     {
30226         ent->takeaction = common_preduck;
30227         ent->velocity.x = ent->velocity.z = 0;
30228         ent->ducking = DUCK_PREPARED;
30229         ent->idling = IDLING_NONE;
30230         ent_set_anim(ent, ANI_DUCKING, 0);
30231     }
30232     else
30233     {
30234         doduck(ent);
30235     }
30236 }
30237 
30238 
tryduckrise(entity * ent)30239 void tryduckrise(entity *ent)
30240 {
30241     ent->running = 0;
30242     if(validanim(ent, ANI_DUCKRISE))
30243     {
30244         ent->takeaction = common_idle;
30245         ent->velocity.x = ent->velocity.z = 0;
30246         ent->ducking = DUCK_RISE;
30247         ent->idling = IDLING_NONE;
30248         ent_set_anim(ent, ANI_DUCKRISE, 0);
30249     }
30250     else
30251     {
30252         ent->takeaction = NULL;
30253         ent->idling = IDLING_PREPARED;
30254         common_idle_anim(self);
30255     }
30256 }
30257 
30258 
doduck(entity * ent)30259 void doduck(entity *ent)
30260 {
30261     ent->takeaction = NULL;
30262     ent->velocity.x = ent->velocity.z = 0;
30263     ent->ducking = DUCK_ACTIVE;
30264     ent->idling |= IDLING_PREPARED;
30265     ent_set_anim(ent, ANI_DUCK, 0);
30266 }
30267 
30268 
common_prejump()30269 void common_prejump()
30270 {
30271     if(self->animating)
30272     {
30273         return;
30274     }
30275     dojump(self->jump.velocity.y, self->jump.velocity.x, self->jump.velocity.z, self->jump.animation_id);
30276 }
30277 
30278 
tryjump(float jumpv,float jumpx,float jumpz,int animation_id)30279 void tryjump(float jumpv, float jumpx, float jumpz, int animation_id)
30280 {
30281     self->jump.velocity.y = jumpv;
30282     self->jump.velocity.x = jumpx;
30283     self->jump.velocity.z = jumpz;
30284     self->jump.animation_id = animation_id;
30285 
30286     self->ducking = DUCK_NONE;
30287     if(validanim(self, ANI_JUMPDELAY))
30288     {
30289         self->takeaction = common_prejump;
30290         self->velocity.x = self->velocity.z = 0;
30291 
30292         self->idling = IDLING_NONE;
30293         ent_set_anim(self, ANI_JUMPDELAY, 0);
30294     }
30295     else
30296     {
30297         dojump(jumpv, jumpx, jumpz, animation_id);
30298     }
30299 }
30300 
30301 
dojump(float jumpv,float jumpx,float jumpz,int animation_id)30302 void dojump(float jumpv, float jumpx, float jumpz, int animation_id)
30303 {
30304     entity *dust;
30305 
30306     self->takeaction = common_jump;
30307 
30308     if(SAMPLE_JUMP >= 0)
30309     {
30310         sound_play_sample(SAMPLE_JUMP, 0, savedata.effectvol, savedata.effectvol, 100);
30311     }
30312 
30313     //Spawn jumpstart dust.
30314     if(self->modeldata.dust.jump_start >= 0)
30315     {
30316         dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_start, NULL);
30317         if(dust)
30318         {
30319             dust->spawntype = SPAWN_TYPE_DUST_JUMP;
30320             dust->base = self->position.y;
30321             dust->autokill |= AUTOKILL_ANIMATION_COMPLETE;
30322             execute_onspawn_script(dust);
30323         }
30324     }
30325 
30326     set_jumping(self);
30327 
30328     toss(self, jumpv);
30329 
30330     if(self->direction == DIRECTION_LEFT)
30331     {
30332         self->velocity.x = -jumpx;
30333     }
30334     else
30335     {
30336         self->velocity.x = jumpx;
30337     }
30338 
30339     self->velocity.z = jumpz;
30340     ent_set_anim(self, animation_id, 0);
30341 }
30342 
30343 // Function created to combine the action taken if either picking up an item, or running into an item that is a
30344 // SUBTYPE_TOUCH, executing the appropriate action based on which type of item is picked up
didfind_item(entity * other)30345 void didfind_item(entity *other)
30346 {
30347     // Function that takes care of items when picked up
30348     set_opponent(self, other);
30349 
30350     //for reload weapons that are guns(no knife) we use this items reload for ours shot at max and shootnum in items for get a amount of shoots by tails
30351     if(other->modeldata.reload)
30352     {
30353         if(self->weapent && self->weapent->modeldata.typeshot)
30354         {
30355             self->weapent->modeldata.shootnum += other->modeldata.reload;
30356 
30357             if(SAMPLE_GET >= 0)
30358             {
30359                 sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
30360             }
30361         }
30362         else
30363         {
30364             addscore(self->playerindex, other->modeldata.score);
30365             if(SAMPLE_GET2 >= 0)
30366             {
30367                 sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
30368             }
30369         }
30370     }
30371     //end of weapons items section
30372     else if(other->modeldata.score)
30373     {
30374         addscore(self->playerindex, other->modeldata.score);
30375         if(SAMPLE_GET2 >= 0)
30376         {
30377             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
30378         }
30379     }
30380     else if(other->energy_state.health_current)
30381     {
30382         self->energy_state.health_current += other->energy_state.health_current;
30383 
30384         if(self->energy_state.health_current > self->modeldata.health)
30385         {
30386             self->energy_state.health_current = self->modeldata.health;
30387         }
30388 
30389         other->energy_state.health_current = 0;
30390 
30391         if(SAMPLE_GET >= 0)
30392         {
30393             sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
30394         }
30395     }
30396     else if(other->modeldata.mp)
30397     {
30398         self->energy_state.mp_current += other->modeldata.mp;
30399 
30400         if(self->energy_state.mp_current > self->modeldata.mp)
30401         {
30402             self->energy_state.mp_current = self->modeldata.mp;
30403         }
30404 
30405         other->energy_state.mp_current = 0;
30406         sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
30407     }
30408     else if(stricmp(other->modeldata.name, "Time") == 0)
30409     {
30410         timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
30411 
30412         if(SAMPLE_GET2 >= 0)
30413         {
30414             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
30415         }
30416     }
30417     else if(other->modeldata.makeinv)
30418     {
30419         // Mar 2, 2005 - New item makes player invincible
30420         self->invincible |= INVINCIBLE_INTANGIBLE;
30421         self->invinctime = _time + ABS(other->modeldata.makeinv);
30422         self->blink = (other->modeldata.makeinv > 0);
30423 
30424         if(SAMPLE_GET2 >= 0)
30425         {
30426             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
30427         }
30428     }
30429     else if(other->modeldata.smartbomb)
30430     {
30431         // Damages everything on the screen
30432         smart_bomb(self, other->modeldata.smartbomb);
30433 
30434         if(SAMPLE_GET2 >= 0)
30435         {
30436             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
30437         }
30438     }
30439     else if(other->modeldata.subtype == SUBTYPE_WEAPON)
30440     {
30441         dropweapon(0);
30442         self->weapent = other;
30443         set_weapon(self, other->modeldata.weapnum, 0);
30444 
30445         if(self->modeldata.animal)  // UTunnels: well, ride, not get. :)
30446         {
30447             self->direction = other->direction;
30448             self->position.x = other->position.x;
30449             self->position.z = other->position.z;
30450         }
30451 
30452         if(!other->modeldata.typeshot && self->modeldata.typeshot)
30453         {
30454             other->modeldata.typeshot = 1;
30455         }
30456 
30457         if(SAMPLE_GET >= 0)
30458         {
30459             sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
30460         }
30461     }
30462     else if(other->modeldata.subtype == SUBTYPE_PROJECTILE)
30463     {
30464         dropweapon(0);
30465         self->weapent = other;
30466 
30467         if(SAMPLE_GET >= 0)
30468         {
30469             sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
30470         }
30471     }
30472     else if(other->modeldata.credit)
30473     {
30474         if(!noshare)
30475         {
30476             credits++;
30477         }
30478         else
30479         {
30480             player[self->playerindex].credits++;
30481         }
30482 
30483         if(SAMPLE_1UP >= 0)
30484         {
30485             sound_play_sample(SAMPLE_1UP, 0, savedata.effectvol, savedata.effectvol, 100);
30486         }
30487     }
30488     else
30489     {
30490         // Must be a 1up then.
30491         player[self->playerindex].lives++;
30492 
30493         if(SAMPLE_1UP >= 0)
30494         {
30495             sound_play_sample(SAMPLE_1UP, 0, savedata.effectvol, savedata.effectvol, 100);
30496         }
30497     }
30498 
30499     if(other->modeldata.subtype != SUBTYPE_WEAPON && other->modeldata.subtype != SUBTYPE_PROJECTILE)
30500     {
30501         other->takeaction = suicide;
30502         if(!other->modeldata.instantitemdeath)
30503         {
30504             other->nextthink = _time + GAME_SPEED * 3;
30505         }
30506     }
30507     else
30508     {
30509         other->nextthink = other->nextanim = _time + GAME_SPEED * 999999;
30510     }
30511     other->position.z = ITEM_HIDE_POSITION_Z;
30512 }
30513 
player_fall_check()30514 void player_fall_check()
30515 {
30516     if(autoland != 2 && (player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_JUMP)) == (FLAG_MOVEUP | FLAG_JUMP))
30517     {
30518 		// mark it, so we will play land animation when hit the ground.
30519         self->damage_on_landing.attack_force = ATTACK_FORCE_LAND_COMMAND;
30520     }
30521 }
30522 
player_grab_check()30523 void player_grab_check()
30524 {
30525     entity *other = self->link;
30526 
30527     if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
30528     {
30529         return;
30530     }
30531 
30532     if(self->base != other->base)
30533     {
30534         // Change this from ->position.y to ->base
30535         self->takeaction = NULL;
30536         ent_unlink(self);
30537         set_idle(self);
30538         return;
30539     }
30540 
30541     if(player_check_special())
30542     {
30543         return;
30544     }
30545 
30546     if(!nolost && self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
30547     {
30548         dropweapon(1);
30549     }
30550 
30551     // grabturn code
30552     if(self->animation == self->modeldata.animation[ANI_GRABTURN])
30553     {
30554         // still turning? don't bother with anything else
30555         if(self->animating)
30556         {
30557             return;
30558         }
30559 
30560         // done turning? switch directions and return to grab animation
30561         else
30562         {
30563             if(self->direction == DIRECTION_RIGHT)
30564             {
30565                 self->direction = DIRECTION_LEFT;
30566                 other->direction = DIRECTION_RIGHT;
30567             }
30568             else
30569             {
30570                 self->direction = DIRECTION_RIGHT;
30571                 other->direction = DIRECTION_LEFT;
30572             }
30573             other->position.x = self->position.x + (((self->direction * 2) - 1) * self->modeldata.grabdistance);
30574             ent_set_anim(self, ANI_GRAB, 0);
30575             set_pain(other, -1, 0);
30576             update_frame(self, self->animation->numframes - 1);
30577             update_frame(other, other->animation->numframes - 1);
30578         }
30579     }
30580 
30581     self->attacking = ATTACKING_NONE; //for checking
30582     self->grabwalking = 0;
30583 
30584 	// Move key opposite vs. dicretion?
30585     if(self->direction == DIRECTION_RIGHT ? (player[self->playerindex].keys & FLAG_MOVELEFT) : (player[self->playerindex].keys & FLAG_MOVERIGHT))
30586     {
30587         // initiating grabturn
30588         if(self->modeldata.grabturn)
30589         {
30590             // start animation if it exists...
30591             if(validanim(self, ANI_GRABTURN))
30592             {
30593                 ent_set_anim(self, ANI_GRABTURN, 0);
30594                 if(validanim(other, ANI_GRABBEDTURN))
30595                 {
30596                     ent_set_anim(other, ANI_GRABBEDTURN, 0);
30597                 }
30598                 else if(validanim(other, ANI_GRABBED))
30599                 {
30600                     ent_set_anim(other, ANI_GRABBED, 0);
30601                 }
30602                 else
30603                 {
30604                     if ( ((!other->direction && self->position.x > other->position.x) || (other->direction && self->position.x < other->position.x)) && validanim(other, ANI_BACKPAIN) ) ent_set_anim(other, ANI_BACKPAIN, 0);
30605                     else ent_set_anim(other, ANI_PAIN, 0);
30606                 }
30607                 other->velocity.x = other->velocity.z = self->velocity.x = self->velocity.z = 0;
30608                 other->position.x = self->position.x;
30609                 return;
30610             }
30611 
30612             // otherwise, just turn around
30613             else
30614             {
30615                 if(self->direction == DIRECTION_RIGHT)
30616                 {
30617                     self->direction = DIRECTION_LEFT;
30618                     other->direction = DIRECTION_RIGHT;
30619                 }
30620                 else
30621                 {
30622                     self->direction = DIRECTION_RIGHT;
30623                     other->direction = DIRECTION_LEFT;
30624                 }
30625                 ent_set_anim(self, ANI_GRAB, 0);
30626                 set_pain(other, -1, 0);
30627                 update_frame(self, self->animation->numframes - 1);
30628                 update_frame(other, other->animation->numframes - 1);
30629                 other->position.x = self->position.x + (((self->direction * 2) - 1) * self->modeldata.grabdistance);
30630             }
30631         }
30632         else if(!validanim(self, ANI_GRABWALK) && _time > self->releasetime)
30633         {
30634             // Release
30635             self->takeaction = NULL;
30636             ent_unlink(self);
30637             set_idle(self);
30638             return;
30639         }
30640     }
30641     else
30642     {
30643         self->releasetime = _time + (GAME_SPEED / 2);
30644     }
30645 
30646     if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
30647             (self->direction ?
30648              (player[self->playerindex].keys & FLAG_MOVELEFT) :
30649              (player[self->playerindex].keys & FLAG_MOVERIGHT)))
30650     {
30651         player[self->playerindex].playkeys -= FLAG_ATTACK;
30652         if(validanim(self, ANI_GRABBACKWARD))
30653         {
30654             dograbattack(GRAB_ACTION_SELECT_BACKWARD);
30655         }
30656         else if(validanim(self, ANI_THROW))
30657         {
30658             if(self->modeldata.throwframewait >= 0)
30659             {
30660                 doprethrow();
30661             }
30662             else
30663             {
30664                 dothrow();
30665             }
30666         }
30667         else
30668         {
30669             dograbattack(GRAB_ACTION_SELECT_ATTACK);
30670         }
30671     }
30672     // grab forward
30673     else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
30674             validanim(self, ANI_GRABFORWARD) &&
30675             (!self->direction ?
30676              (player[self->playerindex].keys & FLAG_MOVELEFT) :
30677              (player[self->playerindex].keys & FLAG_MOVERIGHT)))
30678     {
30679         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
30680         dograbattack(GRAB_ACTION_SELECT_FORWARD);
30681     }
30682     // grab up
30683     else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
30684             validanim(self, ANI_GRABUP) && (player[self->playerindex].keys & FLAG_MOVEUP))
30685     {
30686         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
30687         dograbattack(GRAB_ACTION_SELECT_UP);
30688     }
30689     // grab down
30690     else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
30691             validanim(self, ANI_GRABDOWN) && (player[self->playerindex].keys & FLAG_MOVEDOWN))
30692     {
30693         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
30694         dograbattack(GRAB_ACTION_SELECT_DOWN);
30695     }
30696     // normal grab attack
30697     else if((player[self->playerindex].playkeys & FLAG_ATTACK) && validanim(self, ANI_GRABATTACK))
30698     {
30699         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
30700         dograbattack(GRAB_ACTION_SELECT_ATTACK);
30701     }
30702     // grab attack finisher
30703     else if(player[self->playerindex].playkeys & (FLAG_JUMP | FLAG_ATTACK))
30704     {
30705         player[self->playerindex].playkeys &= ~(FLAG_JUMP | FLAG_ATTACK);
30706 
30707         // Perform final blow
30708         if(validanim(self, ANI_GRABATTACK2) || validanim(self, ANI_ATTACK3))
30709         {
30710             dograbattack(GRAB_ACTION_SELECT_FINISH);
30711         }
30712         else
30713         {
30714             self->attacking = ATTACKING_ACTIVE;
30715             memset(self->combostep, 0, sizeof(*self->combostep) * 5);
30716             self->takeaction = common_grabattack;
30717             tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, 0, ANI_JUMP);
30718         }
30719     }
30720 
30721     // grab walk code
30722     else if(validanim(self, ANI_GRABWALK) // check if grabwalk animation exists
30723 
30724             // if entity is still animating anything besides a grabwalk variant, don't let them move
30725             && (!self->animating || self->animation == self->modeldata.animation[ANI_GRABWALK]
30726                 || self->animation == self->modeldata.animation[ANI_GRABWALKUP]
30727                 || self->animation == self->modeldata.animation[ANI_GRABWALKDOWN]
30728                 || self->animation == self->modeldata.animation[ANI_GRABBACKWALK]))
30729     {
30730 
30731         // z axis movement
30732         if(PLAYER_MIN_Z != PLAYER_MAX_Z)
30733         {
30734             if(player[self->playerindex].keys & FLAG_MOVEUP)
30735             {
30736                 if(self->modeldata.grabwalkspeed)
30737                 {
30738                     self->velocity.z = -self->modeldata.grabwalkspeed / 2;
30739                 }
30740                 else
30741                 {
30742                     self->velocity.z = -self->modeldata.speed / 2;
30743                 }
30744             }
30745             else if(player[self->playerindex].keys & FLAG_MOVEDOWN)
30746             {
30747                 if(self->modeldata.grabwalkspeed)
30748                 {
30749                     self->velocity.z = self->modeldata.grabwalkspeed / 2;
30750                 }
30751                 else
30752                 {
30753                     self->velocity.z = self->modeldata.speed / 2;
30754                 }
30755             }
30756             else if(!(player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
30757             {
30758                 self->velocity.z = 0;
30759             }
30760         }
30761 
30762         // x axis movement
30763         if(player[self->playerindex].keys & FLAG_MOVELEFT)
30764         {
30765             if(self->modeldata.grabwalkspeed)
30766             {
30767                 self->velocity.x = -self->modeldata.grabwalkspeed;
30768             }
30769             else
30770             {
30771                 self->velocity.x = -self->modeldata.speed;
30772             }
30773         }
30774 
30775         else if(player[self->playerindex].keys & FLAG_MOVERIGHT)
30776         {
30777             if(self->modeldata.grabwalkspeed)
30778             {
30779                 self->velocity.x = self->modeldata.grabwalkspeed;
30780             }
30781             else
30782             {
30783                 self->velocity.x = self->modeldata.speed;
30784             }
30785         }
30786         else if(!((player[self->playerindex].keys & FLAG_MOVELEFT) || (player[self->playerindex].keys & FLAG_MOVERIGHT)) )
30787         {
30788             self->velocity.x = 0;
30789         }
30790 
30791         // setting the animations based on the velocity set above
30792         if(self->velocity.x || self->velocity.z)
30793         {
30794             if(((self->velocity.x > 0 && self->direction == DIRECTION_LEFT) || (self->velocity.x < 0 && self->direction == DIRECTION_RIGHT)) && validanim(self, ANI_GRABBACKWALK))
30795             {
30796                 ent_set_anim(self, ANI_GRABBACKWALK, 0);
30797             }
30798             else if(self->velocity.z < 0 && validanim(self, ANI_GRABWALKUP))
30799             {
30800                 ent_set_anim(self, ANI_GRABWALKUP, 0);
30801             }
30802             else if(self->velocity.z > 0 && validanim(self, ANI_GRABWALKDOWN))
30803             {
30804                 ent_set_anim(self, ANI_GRABWALKDOWN, 0);
30805             }
30806             else
30807             {
30808                 ent_set_anim(self, ANI_GRABWALK, 0);
30809             }
30810             if(self->animation == self->modeldata.animation[ANI_GRABWALKUP] && validanim(other, ANI_GRABBEDWALKUP))
30811             {
30812                 ent_set_anim(other, ANI_GRABBEDWALKUP, 0);
30813             }
30814             else if(self->animation == self->modeldata.animation[ANI_GRABWALKDOWN] && validanim(other, ANI_GRABBEDWALKDOWN))
30815             {
30816                 ent_set_anim(other, ANI_GRABBEDWALKDOWN, 0);
30817             }
30818             else if(self->animation == self->modeldata.animation[ANI_GRABBACKWALK] && validanim(other, ANI_GRABBEDBACKWALK))
30819             {
30820                 ent_set_anim(other, ANI_GRABBEDBACKWALK, 0);
30821             }
30822             else if(validanim(other, ANI_GRABBEDWALK))
30823             {
30824                 ent_set_anim(other, ANI_GRABBEDWALK, 0);
30825             }
30826             else if (validanim(other, ANI_GRABBED))
30827             {
30828                 ent_set_anim(other, ANI_GRABBED, 0);
30829             }
30830             else
30831             {
30832                 ent_set_anim(other, ANI_PAIN, 0);
30833             }
30834         }
30835         else
30836         {
30837             ent_set_anim(self, ANI_GRAB, 0);
30838             if (validanim(other, ANI_GRABBED))
30839             {
30840                 ent_set_anim(other, ANI_GRABBED, 0);
30841             }
30842             else
30843             {
30844                 ent_set_anim(other, ANI_PAIN, 0);
30845             }
30846         }
30847         // use check_link_move to set velocity, don't change it here
30848         other->velocity.z = other->velocity.x = 0;
30849         self->grabwalking = 1;
30850     }
30851 
30852     if(self->attacking != ATTACKING_NONE)
30853     {
30854         self->releasetime = _time + (GAME_SPEED / 2);    // reset releasetime when do collision
30855     }
30856 }
30857 
player_walkoff_check()30858 void player_walkoff_check()
30859 {
30860     if(self->modeldata.walkoffmovex & 1) //flip?
30861     {
30862         if((player[self->playerindex].keys & FLAG_MOVELEFT))
30863         {
30864             self->direction = DIRECTION_LEFT;
30865         }
30866         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
30867         {
30868             self->direction = DIRECTION_RIGHT;
30869         }
30870     }
30871     if(self->modeldata.walkoffmovex & 2) //move?
30872     {
30873         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
30874                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
30875         {
30876             self->velocity.x = -self->velocity.x;
30877         }
30878     }
30879     if(self->modeldata.walkoffmovex & 4) //Move x if vertical jump?
30880     {
30881         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
30882                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
30883         {
30884             self->velocity.x = -self->velocity.x;
30885         }
30886 
30887         if((player[self->playerindex].keys & FLAG_MOVELEFT) && (!self->velocity.x))
30888         {
30889             self->velocity.x -= self->modeldata.speed;
30890         }
30891         else if((player[self->playerindex].keys & FLAG_MOVERIGHT) && (!self->velocity.x))
30892         {
30893             self->velocity.x = self->modeldata.speed;
30894         }
30895     }
30896     if(self->modeldata.walkoffmovez & 2) //z move?
30897     {
30898         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
30899                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
30900         {
30901             self->velocity.z = -self->velocity.z;
30902         }
30903     }
30904     if(self->modeldata.walkoffmovez & 4) //Move z if vertical jump?
30905     {
30906         if((player[self->playerindex].keys & FLAG_MOVELEFT))
30907         {
30908             self->direction = DIRECTION_LEFT;
30909         }
30910         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
30911         {
30912             self->direction = DIRECTION_RIGHT;
30913         }
30914 
30915         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
30916                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
30917         {
30918             self->velocity.z = -self->velocity.z;
30919         }
30920 
30921         if((player[self->playerindex].keys & FLAG_MOVEUP) && (!self->velocity.z))
30922         {
30923             self->velocity.z -= (0.5 * self->modeldata.speed);
30924         }
30925         else if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (!self->velocity.z))
30926         {
30927             self->velocity.z = (0.5 * self->modeldata.speed);
30928         }
30929     }
30930 
30931     return;
30932 }
30933 
player_jump_check()30934 void player_jump_check()
30935 {
30936     int candospecial = 0;
30937 
30938     if(!noaircancel || !self->animating || self->animnum == self->jump.animation_id)
30939     {
30940         //air special, copied and changed from Fugue's code
30941         if((!level->nospecial || level->nospecial == 3) && player[self->playerindex].playkeys & FLAG_SPECIAL)
30942         {
30943 
30944             if(validanim(self, ANI_JUMPSPECIAL))
30945             {
30946                 if(self->modeldata.animation[ANI_JUMPSPECIAL]->energycost && check_energy(COST_CHECK_MP, ANI_JUMPSPECIAL))
30947                 {
30948                     if(!healthcheat)
30949                     {
30950                         self->energy_state.mp_current -= self->modeldata.animation[ANI_JUMPSPECIAL]->energycost->cost;
30951                     }
30952                     candospecial = 1;
30953                 }
30954                 else if(self->modeldata.animation[ANI_JUMPSPECIAL]->energycost && check_energy(COST_CHECK_HP, ANI_JUMPSPECIAL))
30955                 {
30956                     if(!healthcheat)
30957                     {
30958                         self->energy_state.health_current -= self->modeldata.animation[ANI_JUMPSPECIAL]->energycost->cost;
30959                     }
30960                     candospecial = 1;
30961                 }
30962                 else if(validanim(self, ANI_JUMPCANT))
30963                 {
30964                     ent_set_anim(self, ANI_JUMPCANT, 0);
30965                     self->velocity.y = 0;
30966                 }
30967 
30968                 if(candospecial)
30969                 {
30970                     player[self->playerindex].playkeys &= ~FLAG_SPECIAL;
30971                     self->attacking = ATTACKING_ACTIVE;
30972                     self->velocity.x = self->velocity.z = 0;                         // Kill movement when the special starts
30973                     self->velocity.y = 0;
30974                     ent_set_anim(self, ANI_JUMPSPECIAL, 0);
30975                 }
30976             }
30977         }//end of jumpspecial
30978 
30979         //jumpattacks, up down forward normal....we don't check energy cost
30980         else if(player[self->playerindex].playkeys & FLAG_ATTACK)
30981         {
30982             player[self->playerindex].playkeys &= ~FLAG_ATTACK;
30983             self->attacking = ATTACKING_ACTIVE;
30984 
30985             if((player[self->playerindex].keys & FLAG_MOVEDOWN) && validanim(self, ANI_JUMPATTACK2))
30986             {
30987                 ent_set_anim(self, ANI_JUMPATTACK2, 0);
30988             }
30989             else if((player[self->playerindex].keys & FLAG_MOVEUP) && validanim(self, ANI_JUMPATTACK3))
30990             {
30991                 ent_set_anim(self, ANI_JUMPATTACK3, 0);
30992             }
30993             else if(self->running && validanim(self, ANI_RUNJUMPATTACK))
30994             {
30995                 ent_set_anim(self, ANI_RUNJUMPATTACK, 0);    // Added so an extra strong jump attack can be executed
30996             }
30997             else if(self->velocity.x != 0 && validanim(self, ANI_JUMPFORWARD))
30998             {
30999                 ent_set_anim(self, ANI_JUMPFORWARD, 0);    // If moving and set, do this attack
31000             }
31001             else if(validanim(self, ANI_JUMPATTACK))
31002             {
31003                 ent_set_anim(self, ANI_JUMPATTACK, 0);
31004             }
31005         }//end of jumpattack
31006     }
31007     if(self->modeldata.jumpmovex & 1) //flip?
31008     {
31009         if((player[self->playerindex].keys & FLAG_MOVELEFT))
31010         {
31011             self->direction = DIRECTION_LEFT;
31012         }
31013         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
31014         {
31015             self->direction = DIRECTION_RIGHT;
31016         }
31017     }
31018     if(self->modeldata.jumpmovex & 2) //move?
31019     {
31020         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
31021                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
31022         {
31023             self->velocity.x = -self->velocity.x;
31024         }
31025     }
31026     if(self->modeldata.jumpmovex & 4) //Move x if vertical jump?
31027     {
31028         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
31029                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
31030         {
31031             self->velocity.x = -self->velocity.x;
31032         }
31033 
31034         if((player[self->playerindex].keys & FLAG_MOVELEFT) && (!self->velocity.x))
31035         {
31036             self->velocity.x -= self->modeldata.speed;
31037         }
31038         else if((player[self->playerindex].keys & FLAG_MOVERIGHT) && (!self->velocity.x))
31039         {
31040             self->velocity.x = self->modeldata.speed;
31041         }
31042     }
31043     if(self->modeldata.jumpmovez & 2) //z move?
31044     {
31045         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
31046                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
31047         {
31048             self->velocity.z = -self->velocity.z;
31049         }
31050     }
31051     if(self->modeldata.jumpmovez & 4) //Move z if vertical jump?
31052     {
31053         if((player[self->playerindex].keys & FLAG_MOVELEFT))
31054         {
31055             self->direction = DIRECTION_LEFT;
31056         }
31057         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
31058         {
31059             self->direction = DIRECTION_RIGHT;
31060         }
31061 
31062         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
31063                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
31064         {
31065             self->velocity.z = -self->velocity.z;
31066         }
31067 
31068         if((player[self->playerindex].keys & FLAG_MOVEUP) && (!self->velocity.z))
31069         {
31070             self->velocity.z -= (0.5 * self->modeldata.speed);
31071         }
31072         else if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (!self->velocity.z))
31073         {
31074             self->velocity.z = (0.5 * self->modeldata.speed);
31075         }
31076     }
31077 
31078     return;
31079 }
31080 
player_pain_check()31081 void player_pain_check()
31082 {
31083     if(player_check_special())
31084     {
31085         self->inpain = 0;
31086         self->rising = RISING_NONE;
31087         self->ducking = DUCK_NONE;
31088         self->inbackpain = 0;
31089     }
31090 }
31091 
31092 // check riseattack input up+attack
player_lie_check()31093 void player_lie_check()
31094 {
31095     if(validanim(self, ANI_RISEATTACK) &&
31096             (player[self->playerindex].playkeys & FLAG_ATTACK) &&
31097             (player[self->playerindex].keys & FLAG_MOVEUP) &&
31098             (self->energy_state.health_current > 0 && _time > self->staydown.riseattack_stall))
31099     {
31100         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
31101         if((player[self->playerindex].keys & FLAG_MOVELEFT))
31102         {
31103             self->direction = DIRECTION_LEFT;
31104         }
31105         if((player[self->playerindex].keys & FLAG_MOVERIGHT))
31106         {
31107             self->direction = DIRECTION_RIGHT;
31108         }
31109         self->stalltime = 0;
31110         set_riseattack(self, self->last_damage_type, 0);
31111     }
31112 }
31113 
player_charge_check()31114 void player_charge_check()
31115 {
31116     if(!((player[self->playerindex].keys & FLAG_JUMP) &&
31117             (player[self->playerindex].keys & FLAG_SPECIAL)))
31118     {
31119         self->takeaction = NULL;
31120         self->charging = 0;
31121         set_idle(self);
31122     }
31123 }
31124 
31125 
31126 // make a function so enemies can use
31127 // UT: jumphack is a temporary fix for jump cancel
check_costmove(int s,int fs,int jumphack)31128 int check_costmove(int s, int fs, int jumphack)
31129 {
31130     if(((fs == 1 && level->nospecial < 2) || (fs == 0 && level->nospecial == 0) || (fs == 0 && level->nospecial == 3)) &&
31131             (check_energy(COST_CHECK_HP, s) ||
31132              check_energy(COST_CHECK_MP, s))  )
31133     {
31134         if(!jumphack)
31135         {
31136             self->takeaction = common_attack_proc;
31137         }
31138         if(!nocost && !healthcheat)
31139         {
31140             if(self->modeldata.animation[s]->energycost)
31141             {
31142                 if(check_energy(COST_CHECK_MP, s))
31143                 {
31144                     self->energy_state.mp_current -= self->modeldata.animation[s]->energycost->cost;
31145                 }
31146                 else
31147                 {
31148                     self->energy_state.health_current -= self->modeldata.animation[s]->energycost->cost;
31149                 }
31150             }
31151         }
31152 
31153 		self->running = 0;
31154         self->velocity.x = self->velocity.z = 0;
31155         set_attacking(self);
31156         self->inpain = 0;
31157         self->rising = RISING_NONE;
31158         self->inbackpain = 0;
31159         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
31160         ent_unlink(self);
31161         ent_set_anim(self, s, 0);
31162         return 1;
31163     }
31164     return 0;
31165 }
31166 
match_combo(int a[],s_player * p,int l)31167 int match_combo(int a[], s_player *p, int l)
31168 {
31169     int j, step;
31170 
31171     for(j = 0; j < l; j++)
31172     {
31173         step = p->combostep - 1 - j;
31174         step = (step + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31175 
31176         // old: !(a[l - 1 - j]&p->combokey[step])
31177         if( ((a[l - 1 - j]&p->combokey[step]) ^ a[l - 1 - j]) ) // if input&combokey == 0 then not good btn
31178         {
31179             return 0;
31180         }
31181     }
31182 
31183     return 1;
31184 }
31185 
31186 
check_combo()31187 int check_combo()
31188 {
31189     int i, maxstep = -1, maxkeys = -1, valid = -1;
31190     s_com *com;
31191     s_player *p;
31192 
31193     p = player + self->playerindex;
31194 
31195     for(i = 0; i < self->modeldata.specials_loaded; i++)
31196     {
31197         com = self->modeldata.special + i;
31198 
31199         if(self->animation->cancel &&
31200                 (self->animnum != com->cancel ||
31201                  com->frame.min > self->animpos ||
31202                  com->frame.max < self->animpos ||
31203                  self->animation->animhits < com->hits))
31204         {
31205             continue;
31206         }
31207         else if(!self->animation->cancel &&
31208                 (com->cancel || !self->idling || diff(self->position.y, self->base) > 1) )
31209         {
31210             continue;
31211         }
31212 
31213         // find the longest possible combo with more keys pressed concurrently
31214         if( com->steps >= maxstep && com->numkeys > maxkeys &&
31215                 validanim(self, com->anim) &&
31216                 (check_energy(COST_CHECK_MP, com->anim) || check_energy(COST_CHECK_HP, com->anim)) &&
31217                 match_combo(com->input, p, com->steps))
31218         {
31219             // combo valid! but which better? The longest combo that has with more keys pressed concurrently
31220             valid = com->anim;
31221             maxstep = com->steps;
31222             maxkeys = com->numkeys;
31223         }
31224     }//end of for
31225 
31226     if(valid >= 0 && check_costmove(valid, 1, self->jumping))
31227     {
31228         return 1;
31229     }
31230 
31231     return 0;
31232 }
31233 
player_preinput()31234 int player_preinput()
31235 {
31236     if(player[self->playerindex].playkeys)
31237     {
31238         if(check_combo())
31239         {
31240             player[self->playerindex].playkeys &= ~FLAG_CONTROLKEYS;
31241             return 1;
31242         }
31243     }
31244     return 0;
31245 }
31246 
player_think()31247 void player_think()
31248 {
31249     int action = 0;		// 1=walking, 2=up, 3=down, 4=running
31250     int bkwalk = 0;   //backwalk
31251     int runx, runz, movex, movez;
31252     int t, t2;
31253     entity *other = NULL;
31254     float altdiff;
31255     int notinair;
31256 
31257     static int ll[] = {FLAG_MOVELEFT, FLAG_MOVELEFT};
31258     static int rr[] = {FLAG_MOVERIGHT, FLAG_MOVERIGHT};
31259     static int uu[] = {FLAG_MOVEUP, FLAG_MOVEUP};
31260     static int dd[] = {FLAG_MOVEDOWN, FLAG_MOVEDOWN};
31261     static int ba[] = {FLAG_BACKWARD, FLAG_ATTACK};
31262 
31263     int oldrunning = self->running;
31264     int pli = self->playerindex;
31265     s_player *pl = player + pli;
31266 
31267 
31268     if(pl->ent != self || self->dead)
31269     {
31270         return;
31271     }
31272 
31273     // check endlevel item
31274     if((other = find_ent_here(self, self->position.x, self->position.z, TYPE_ENDLEVEL, NULL)) && diff(self->position.y, other->position.y) <= 0.1)
31275     {
31276         int no_reached_flag = 0, sum_reached = 0;;
31277         int i;
31278 
31279         for (i = 0; i < MAX_PLAYERS; i++)
31280         {
31281             if (!reached[i]) ++no_reached_flag;
31282         }
31283         no_reached_flag = (no_reached_flag >= MAX_PLAYERS) ? 1 : 0;
31284 
31285         if(no_reached_flag)
31286         {
31287             addscore(pli, other->modeldata.score);
31288         }
31289         reached[pli] = 1;
31290 
31291         for (i = 0; i < MAX_PLAYERS; i++)
31292         {
31293             sum_reached += reached[i];
31294         }
31295 
31296         if (!other->modeldata.subtype || (other->modeldata.subtype == SUBTYPE_BOTH && sum_reached >= (count_ents(TYPE_PLAYER))))
31297         {
31298             level_completed = 1;
31299 
31300             if(other->modeldata.branch)
31301             {
31302                 strncpy( branch_name, other->modeldata.branch, MAX_NAME_LEN);    //now, you can branch to another level
31303             }
31304             return;
31305         }
31306     }
31307 
31308     if(_time > self->rush.time)
31309     {
31310         self->rush.count.current = 0;
31311         self->rush.time = 0;
31312     }
31313 
31314     if(player_preinput())
31315     {
31316         goto endthinkcheck;
31317     }
31318 
31319     if(self->charging)
31320     {
31321         player_charge_check();
31322         goto endthinkcheck;
31323     }
31324 
31325     if(self->inpain || (self->link && !self->grabbing))
31326     {
31327         player_pain_check();
31328         goto endthinkcheck;
31329     }
31330 
31331     // falling? check for landing
31332     if(self->projectile & BLAST_TOSS)
31333     {
31334         player_fall_check();
31335         goto endthinkcheck;
31336     }
31337 
31338     // grab section, dont move if still animating
31339     if(self->grabbing && self->attacking == ATTACKING_NONE && self->takeaction != common_throw_wait)
31340     {
31341         player_grab_check();
31342         goto endthinkcheck;
31343     }
31344 
31345     // jump section
31346     if(self->jumping)
31347     {
31348         player_jump_check();
31349         goto endthinkcheck;
31350     }
31351 
31352     if(self->animnum == ANI_WALKOFF)
31353     {
31354         player_walkoff_check();
31355         goto endthinkcheck;
31356     }
31357 
31358     if(self->drop && self->position.y == self->base && !self->velocity.y)
31359     {
31360         player_lie_check();
31361         goto endthinkcheck;
31362     }
31363 
31364 
31365     // cant do anything if busy
31366     if(!self->idling && !(self->animation->idle && self->animation->idle[self->animpos]))
31367     {
31368         goto endthinkcheck;
31369     }
31370 
31371 
31372     // Check if entity is under a platform
31373     /*if(self->modeldata.subject_to_platform > 0 && (heightvar = self->animation->size.y ? self->animation->size.y : self->modeldata.size.y) &&
31374             validanim(self, ANI_DUCK) && check_platform_between(self->position.x, self->position.z, self->position.y, self->position.y + heightvar, self))
31375     {
31376         self->idling = IDLING_NONE;
31377         self->ducking = DUCK_ACTIVE;
31378         self->takeaction = common_stuck_underneath;
31379         ent_set_anim(self, ANI_DUCK, 0);
31380         goto endthinkcheck;
31381     }*/
31382 
31383     altdiff = diff(self->position.y, self->base);
31384     notinair = (self->landed_on_platform ? altdiff < 5 : altdiff < 2);
31385 
31386     if(pl->playkeys & FLAG_MOVEUP)
31387     {
31388         t = (notinair && match_combo(uu, pl, 2));
31389         if(t && (self->modeldata.runupdown & 2) && validanim(self, ANI_RUN))
31390         {
31391             pl->playkeys &= ~FLAG_MOVEUP;
31392             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31393             self->running = 1;    // Player begins to run
31394         }
31395         else if(t && validanim(self, ANI_ATTACKUP))
31396         {
31397             // New u u combo attack
31398             pl->playkeys &= ~FLAG_MOVEUP;
31399             self->takeaction = common_attack_proc;
31400             set_attacking(self);
31401             self->combostep[0] = 0;
31402             self->velocity.x = self->velocity.z = 0;
31403             ent_set_anim(self, ANI_ATTACKUP, 0);
31404             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS; // this workaround deals default freespecial2
31405             goto endthinkcheck;
31406         }
31407         else if(t && validanim(self, ANI_DODGE))
31408         {
31409             // New dodge move like on SOR3
31410             pl->playkeys &= ~FLAG_MOVEUP;
31411             self->takeaction = common_dodge;
31412             self->combostep[0] = 0;
31413             self->idling = IDLING_NONE;
31414             self->velocity.z = -self->modeldata.speed * 1.75;
31415             self->velocity.x = 0;// OK you can use jumpframe to modify this anyway
31416             ent_set_anim(self, ANI_DODGE, 0);
31417             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31418             goto endthinkcheck;
31419         }
31420     }
31421 
31422     if(pl->playkeys & FLAG_MOVEDOWN)
31423     {
31424         t = (notinair && match_combo(dd, pl, 2));
31425         if(t && (self->modeldata.runupdown & 2) && validanim(self, ANI_RUN))
31426         {
31427             pl->playkeys &= ~FLAG_MOVEDOWN;
31428             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31429             self->running = 1;    // Player begins to run
31430         }
31431         else if(t && validanim(self, ANI_ATTACKDOWN))
31432         {
31433             // New d d combo attack
31434             pl->playkeys &= ~FLAG_MOVEDOWN;
31435             self->takeaction = common_attack_proc;
31436             set_attacking(self);
31437             self->velocity.x = self->velocity.z = 0;
31438             self->combostep[0] = 0;
31439             ent_set_anim(self, ANI_ATTACKDOWN, 0);
31440             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31441             goto endthinkcheck;
31442         }
31443         else if(t && validanim(self, ANI_DODGE))
31444         {
31445             // New dodge move like on SOR3
31446             pl->playkeys &= ~FLAG_MOVEDOWN;
31447             self->takeaction = common_dodge;
31448             self->combostep[0] = 0;
31449             self->idling = IDLING_NONE;
31450             self->velocity.z = self->modeldata.speed * 1.75;
31451             self->velocity.x = 0;
31452             ent_set_anim(self, ANI_DODGE, 0);
31453             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31454             goto endthinkcheck;
31455         }
31456     }
31457 
31458     if((pl->playkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)))
31459     {
31460         int t3;
31461 
31462         t = (notinair && ((self->direction == DIRECTION_RIGHT && match_combo(rr, pl, 2)) || (self->direction == DIRECTION_LEFT && match_combo(ll, pl, 2))));
31463         t3 = (notinair && self->modeldata.facing && ((self->direction == DIRECTION_RIGHT && match_combo(ll, pl, 2)) || (self->direction == DIRECTION_LEFT && match_combo(rr, pl, 2))));
31464 
31465         if(t && validanim(self, ANI_RUN))
31466         {
31467             pl->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both
31468             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31469             self->running = 1;    // Player begins to run
31470         }
31471         else if(t3 && validanim(self, ANI_BACKRUN))
31472         {
31473             pl->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both
31474             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31475             self->running = 1;    // Player begins to run
31476         }
31477         else if(t && validanim(self, ANI_ATTACKFORWARD))
31478         {
31479             pl->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT);
31480             self->takeaction = common_attack_proc;
31481             set_attacking(self);
31482             self->velocity.x = self->velocity.z = 0;
31483             self->combostep[0] = 0;
31484             ent_set_anim(self, ANI_ATTACKFORWARD, 0);
31485             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31486             goto endthinkcheck;
31487         }
31488     }
31489 
31490     if(!ajspecial && (pl->playkeys & FLAG_JUMP) && validanim(self, ANI_ATTACKBOTH))
31491     {
31492         if((pl->keys & FLAG_ATTACK) && notinair)
31493         {
31494             pl->playkeys &= ~FLAG_JUMP;
31495             self->takeaction = common_attack_proc;
31496             set_attacking(self);
31497             self->velocity.x = self->velocity.z = 0;
31498             self->combostep[0] = 0;
31499             self->stalltime = 0;    // If attack is pressed, holding down attack to execute attack3 is no longer valid
31500             ent_set_anim(self, ANI_ATTACKBOTH, 0);
31501             goto endthinkcheck;
31502         }
31503     }
31504 
31505     if((pl->playkeys & FLAG_JUMP) &&  validanim(self, ANI_CHARGE))
31506     {
31507         if((pl->keys & FLAG_SPECIAL) && notinair)
31508         {
31509             pl->playkeys &= ~FLAG_JUMP;
31510             self->takeaction = common_charge;
31511             self->combostep[0] = 0;
31512             self->velocity.x = self->velocity.z = 0;
31513             self->stalltime = 0;
31514             set_charging(self);
31515             ent_set_anim(self, ANI_CHARGE, 0);
31516             goto endthinkcheck;
31517         }
31518     }
31519 
31520     if(pl->playkeys & FLAG_SPECIAL)    //    The special button can now be used for freespecials
31521     {
31522         if( validanim(self, ANI_SPECIAL2) && notinair &&
31523                 (self->direction == DIRECTION_LEFT ?
31524                  (pl->keys & FLAG_MOVELEFT) :
31525                  (pl->keys & FLAG_MOVERIGHT))  )
31526         {
31527             if(check_costmove(ANI_SPECIAL2, 0, 0))
31528             {
31529                 pl->playkeys &= ~FLAG_SPECIAL;
31530                 goto endthinkcheck;
31531             }
31532         }
31533 
31534 		// Blocking.
31535         if(validanim(self, ANI_BLOCK) && notinair)
31536         {
31537             pl->playkeys &= ~FLAG_SPECIAL;
31538 
31539 			// Set up flags, action, and block animations.
31540 			do_active_block(self);
31541 
31542 			goto endthinkcheck;
31543         }
31544     }
31545 
31546     if(notinair && player_check_special())
31547     {
31548         goto endthinkcheck;    // So you don't perform specials falling off the edge
31549     }
31550 
31551     if((pl->releasekeys & FLAG_ATTACK))
31552     {
31553         if(self->stalltime && notinair &&
31554                 ((validanim(self, ANI_CHARGEATTACK) && self->stalltime + (GAME_SPEED * self->modeldata.animation[ANI_CHARGEATTACK]->chargetime) < _time) ||
31555                  (!validanim(self, ANI_CHARGEATTACK) && validanim(self, animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1])
31556                   && self->modeldata.chainlength > 0 && self->stalltime + (GAME_SPEED * self->modeldata.animation[animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1]]->chargetime) < _time)))
31557         {
31558             self->takeaction = common_attack_proc;
31559             set_attacking(self);
31560             self->velocity.x = self->velocity.z = 0;
31561 
31562             self->stalltime = 0;
31563             self->combostep[0] = 0;
31564 
31565             if(SAMPLE_PUNCH >= 0)
31566             {
31567                 sound_play_sample(SAMPLE_PUNCH, 0, savedata.effectvol, savedata.effectvol, 100);
31568             }
31569 
31570             if(validanim(self, ANI_CHARGEATTACK))
31571             {
31572                 ent_set_anim(self, ANI_CHARGEATTACK, 0);
31573             }
31574             else if(validanim(self, animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1]))
31575             {
31576                 ent_set_anim(self, animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1], 0);
31577             }
31578             goto endthinkcheck;
31579         }
31580         self->stalltime = 0;
31581     }
31582 
31583     if((pl->playkeys & FLAG_ATTACK) && notinair)
31584     {
31585         pl->playkeys &= ~FLAG_ATTACK;
31586         self->stalltime = 0;    // Disable the attack3 stalltime
31587 
31588         if((self->ducking & DUCK_ACTIVE) && validanim(self, ANI_DUCKATTACK) && PLAYER_MIN_Z == PLAYER_MAX_Z) //pl->keys & FLAG_MOVEDOWN
31589         {
31590             self->takeaction = common_attack_proc;
31591             set_attacking(self);
31592             self->velocity.x = self->velocity.z = 0;
31593             self->combostep[0] = 0;
31594             ent_set_anim(self, ANI_DUCKATTACK, 0);
31595             goto endthinkcheck;
31596         }
31597 
31598         if(self->running && validanim(self, ANI_RUNATTACK))   // New run attack code section
31599         {
31600             self->takeaction = common_attack_proc;
31601             set_attacking(self);
31602             self->velocity.x = self->velocity.z = 0;
31603             self->combostep[0] = 0;
31604             self->running = 0;
31605             ent_set_anim(self, ANI_RUNATTACK, 0);
31606             goto endthinkcheck;
31607         }
31608 
31609         if(validanim(self, ANI_ATTACKBACKWARD) && match_combo(ba, pl, 2))
31610         {
31611             t = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31612             t2 = (pl->combostep - 2 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
31613             if(pl->inputtime[t] - pl->inputtime[t2] < GAME_SPEED / 10)
31614             {
31615                 self->takeaction = common_attack_proc;
31616                 set_attacking(self);
31617                 self->velocity.x = self->velocity.z = 0;
31618                 if(self->direction == DIRECTION_LEFT && (pl->combokey[t2]&FLAG_MOVELEFT))
31619                 {
31620                     self->direction = DIRECTION_RIGHT;
31621                 }
31622                 else if(self->direction == DIRECTION_RIGHT && (pl->combokey[t2]&FLAG_MOVERIGHT))
31623                 {
31624                     self->direction = DIRECTION_LEFT;
31625                 }
31626                 self->combostep[0] = 0;
31627                 ent_set_anim(self, ANI_ATTACKBACKWARD, 0);
31628                 goto endthinkcheck;
31629             }
31630         }
31631 
31632         if( validanim(self, ANI_GET) && (other = find_ent_here(self, self->position.x, self->position.z, TYPE_ITEM, player_test_pickable)) )
31633         {
31634             self->velocity.x = self->velocity.z = 0;
31635             set_getting(self);
31636             self->takeaction = common_get;
31637             ent_set_anim(self, ANI_GET, 0);
31638 
31639             // Item "attacks" collector to make it
31640             // easy to script actions on item pick up.
31641             do_item_script(self, other);
31642 
31643             didfind_item(other);
31644             goto endthinkcheck;
31645         }
31646 
31647         // Use stalltime to charge end-move
31648         self->stalltime = _time;
31649         self->velocity.x = self->velocity.z = 0;
31650 
31651         if( self->weapent &&
31652                 self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
31653                 validanim(self, ANI_THROWATTACK)  )
31654         {
31655             self->takeaction = common_attack_proc;
31656             set_attacking(self);
31657             ent_set_anim(self, ANI_THROWATTACK, 0);
31658             goto endthinkcheck;
31659         }
31660         else if(perform_atchain())
31661         {
31662             if(SAMPLE_PUNCH >= 0 && self->attacking != ATTACKING_NONE)
31663             {
31664                 sound_play_sample(SAMPLE_PUNCH, 0, savedata.effectvol, savedata.effectvol, 100);
31665             }
31666             goto endthinkcheck;
31667         }
31668 
31669     }
31670     // 7-1-2005 spawn projectile end
31671 
31672     if(pl->playkeys & FLAG_JUMP && notinair)
31673     {
31674         // Added !inair(self) so players can't jump when falling into holes
31675         pl->playkeys &= ~FLAG_JUMP;
31676 
31677         if(self->running)
31678         {
31679             //Slide
31680             if((pl->keys & FLAG_MOVEDOWN) && validanim(self, ANI_RUNSLIDE))
31681             {
31682                 self->takeaction = common_attack_proc;
31683                 set_attacking(self);
31684                 self->velocity.x = self->velocity.z = 0;
31685                 self->combostep[0] = 0;
31686                 self->running = 0;
31687                 ent_set_anim(self, ANI_RUNSLIDE, 0);
31688                 goto endthinkcheck;
31689             }
31690 
31691             if(validanim(self, ANI_RUNJUMP))
31692             {
31693                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_RUNJUMP);
31694             }
31695             else if(validanim(self, ANI_FORWARDJUMP))
31696             {
31697                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
31698             }
31699             else if(validanim(self, ANI_JUMP))
31700             {
31701                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
31702             }
31703         }
31704         else
31705         {
31706             //Slide
31707             if((pl->keys & FLAG_MOVEDOWN) && validanim(self, ANI_SLIDE))
31708             {
31709                 self->takeaction = common_attack_proc;
31710                 set_attacking(self);
31711                 self->velocity.x = self->velocity.z = 0;
31712                 self->combostep[0] = 0;
31713                 self->running = 0;
31714                 ent_set_anim(self, ANI_SLIDE, 0);
31715                 goto endthinkcheck;
31716             }
31717 
31718             if(!(pl->keys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)) && validanim(self, ANI_JUMP))
31719             {
31720                 tryjump(self->modeldata.jumpheight, 0, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
31721                 goto endthinkcheck;
31722             }
31723             else if((pl->keys & FLAG_MOVELEFT))
31724             {
31725                 self->direction = DIRECTION_LEFT;
31726             }
31727             else if((pl->keys & FLAG_MOVERIGHT))
31728             {
31729                 self->direction = DIRECTION_RIGHT;
31730             }
31731 
31732             if(validanim(self, ANI_FORWARDJUMP))
31733             {
31734                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
31735             }
31736             else if(validanim(self, ANI_JUMP))
31737             {
31738                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
31739             }
31740         }
31741         return;
31742     }
31743 
31744     //dang long run checking logic
31745     if(self->running)
31746     {
31747         runx = runz = movex = movez = 0;
31748         if(pl->keys & FLAG_MOVEUP)
31749         {
31750             movez--;
31751         }
31752         if(pl->keys & FLAG_MOVEDOWN)
31753         {
31754             movez++;
31755         }
31756         if(pl->keys & FLAG_MOVELEFT)
31757         {
31758             movex--;
31759         }
31760         if(pl->keys & FLAG_MOVERIGHT)
31761         {
31762             movex++;
31763         }
31764         if(oldrunning)
31765         {
31766             if(self->velocity.z < 0)
31767             {
31768                 runz--;
31769             }
31770             else if(self->velocity.z > 0)
31771             {
31772                 runz++;
31773             }
31774             if(self->velocity.x < 0)
31775             {
31776                 runx--;
31777             }
31778             else if(self->velocity.x > 0)
31779             {
31780                 runx++;
31781             }
31782         }
31783         if(!self->modeldata.runupdown)
31784         {
31785             if(movez || !movex)
31786             {
31787                 self->running = 0;
31788             }
31789         }
31790         else if(self->modeldata.runupdown & 4)
31791         {
31792             if(!movex && !movez)
31793             {
31794                 self->running = 0;
31795             }
31796             else if(movex && !movez && runx == -movex)
31797             {
31798                 self->running = 0;
31799             }
31800             else if(movez && !movex && runz == -movez)
31801             {
31802                 self->running = 0;
31803             }
31804             else if(movex && movez && diff(movex, runx) + diff(movez, runz) > 2)
31805             {
31806                 self->running = 0;
31807             }
31808         }
31809         else if(self->modeldata.runupdown)
31810         {
31811             if(!movex || movex == -runx)
31812             {
31813                 self->running = 0;
31814             }
31815         }
31816 
31817     }
31818 
31819     if(PLAYER_MIN_Z != PLAYER_MAX_Z && self->ducking == DUCK_NONE)
31820     {
31821         // More of a platform feel
31822         if(pl->keys & FLAG_MOVEUP)
31823         {
31824             //if(!self->modeldata.runupdown ) self->running = 0;    // Quits running if player presses up (or the up animation exists
31825 
31826             if(validanim(self, ANI_UP) && !self->running)
31827             {
31828                 action = 2;
31829                 self->velocity.z = -self->modeldata.speed / 2;  // Used for up animation
31830             }
31831             else if(self->running)
31832             {
31833                 action = 4;
31834                 self->velocity.z = -self->modeldata.runspeed / 2;  // Moves up at a faster rate running
31835             }
31836             else
31837             {
31838                 action = 1;
31839                 self->velocity.z = -self->modeldata.speed / 2;
31840             }
31841         }
31842         else if(pl->keys & FLAG_MOVEDOWN)
31843         {
31844             //if(!self->modeldata.runupdown ) self->running = 0;    // Quits running if player presses down (or the down animation exists
31845 
31846             if(validanim(self, ANI_DOWN) && !self->running )
31847             {
31848                 action = 3;
31849                 self->velocity.z = self->modeldata.speed / 2;  // Used for down animation
31850             }
31851             else if(self->running)
31852             {
31853                 action = 4;
31854                 self->velocity.z = self->modeldata.runspeed / 2;  // Moves down at a faster rate running
31855             }
31856             else
31857             {
31858                 action = 1;
31859                 self->velocity.z = self->modeldata.speed / 2;
31860             }
31861         }
31862         else if(!(pl->keys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
31863         {
31864             self->velocity.z = 0;
31865         }
31866     }
31867     else if(self->ducking == DUCK_NONE && validanim(self, ANI_DUCK) && pl->keys & FLAG_MOVEDOWN && notinair)
31868     {
31869         tryduck(self);
31870         goto endthinkcheck;
31871     }
31872 
31873     if(pl->keys & FLAG_MOVELEFT && self->ducking == DUCK_NONE)
31874     {
31875         if(self->direction == DIRECTION_RIGHT)
31876         {
31877             //self->running = 0;    // Quits running if player changes direction
31878             if(self->modeldata.turndelay && !self->turntime)
31879             {
31880                 self->turntime = _time + self->modeldata.turndelay;
31881             }
31882             else if(self->turntime && _time >= self->turntime)
31883             {
31884                 self->turntime = 0;
31885                 if(validanim(self, ANI_TURN))
31886                 {
31887                     self->takeaction = common_turn;
31888                     set_turning(self);
31889                     self->velocity.x = self->velocity.z = 0;
31890                     ent_set_anim(self, ANI_TURN, 0);
31891                     goto endthinkcheck;
31892                 }
31893                 self->direction = DIRECTION_LEFT;
31894             }
31895             else if(!self->modeldata.turndelay && validanim(self, ANI_TURN))
31896             {
31897                 self->takeaction = common_turn;
31898                 set_turning(self);
31899                 self->velocity.x = self->velocity.z = 0;
31900                 ent_set_anim(self, ANI_TURN, 0);
31901                 goto endthinkcheck;
31902             }
31903             else if(!self->turntime)
31904             {
31905                 self->direction = DIRECTION_LEFT;
31906             }
31907         }
31908         else
31909         {
31910             self->turntime = 0;
31911         }
31912 
31913         if(self->running)
31914         {
31915             action = 4;
31916             self->velocity.x = -self->modeldata.runspeed;    // If running, player moves at a faster rate
31917         }
31918         else if(action != 2 && action != 3)
31919         {
31920             action = 1;
31921             self->velocity.x = -self->modeldata.speed;
31922         }
31923         else
31924         {
31925             self->velocity.x = -self->modeldata.speed;
31926         }
31927     }
31928     else if(pl->keys & FLAG_MOVERIGHT && self->ducking == DUCK_NONE)
31929     {
31930         if(self->direction == DIRECTION_LEFT)
31931         {
31932             //self->running = 0;    // Quits running if player changes direction
31933             if(self->modeldata.turndelay && !self->turntime)
31934             {
31935                 self->turntime = _time + self->modeldata.turndelay;
31936             }
31937             else if(self->turntime && _time >= self->turntime)
31938             {
31939                 self->turntime = 0;
31940                 if(validanim(self, ANI_TURN))
31941                 {
31942                     self->takeaction = common_turn;
31943                     set_turning(self);
31944                     self->velocity.x = self->velocity.z = 0;
31945                     ent_set_anim(self, ANI_TURN, 0);
31946                     goto endthinkcheck;
31947                 }
31948                 self->direction = DIRECTION_RIGHT;
31949             }
31950             else if(!self->modeldata.turndelay && validanim(self, ANI_TURN))
31951             {
31952                 self->takeaction = common_turn;
31953                 set_turning(self);
31954                 self->velocity.x = self->velocity.z = 0;
31955                 ent_set_anim(self, ANI_TURN, 0);
31956                 goto endthinkcheck;
31957             }
31958             else if(!self->turntime)
31959             {
31960                 self->direction = DIRECTION_RIGHT;
31961             }
31962         }
31963         else
31964         {
31965             self->turntime = 0;
31966         }
31967 
31968         if(self->running)
31969         {
31970             action = 4;
31971             self->velocity.x = self->modeldata.runspeed;    // If running, player moves at a faster rate
31972         }
31973         else if(action != 2 && action != 3)
31974         {
31975             action = 1;
31976             self->velocity.x = self->modeldata.speed;
31977         }
31978         else
31979         {
31980             self->velocity.x = self->modeldata.speed;
31981         }
31982     }
31983     else if(!((pl->keys & FLAG_MOVELEFT) ||
31984               (pl->keys & FLAG_MOVERIGHT)) )
31985     {
31986         //self->running = 0;    // Player let go of left/right and so quits running
31987         self->velocity.x = 0;
31988         self->turntime = 0;
31989     }
31990 
31991     if((other = find_ent_here(self, self->position.x, self->position.z, TYPE_ITEM, player_test_touch))  )
31992     {
31993         do_item_script(self, other);
31994         didfind_item(other);    // Added function to clean code up a bit
31995     }
31996 
31997     if (self->ducking & DUCK_ACTIVE)
31998     {
31999         if (!(pl->keys & FLAG_MOVEDOWN))
32000         {
32001             tryduckrise(self);
32002             goto endthinkcheck;
32003         }
32004         if(pl->keys & FLAG_MOVELEFT)
32005         {
32006             if(self->direction == DIRECTION_RIGHT)
32007             {
32008                 self->direction = DIRECTION_LEFT;
32009             }
32010         }
32011         else if(pl->keys & FLAG_MOVERIGHT)
32012         {
32013             if(self->direction == DIRECTION_LEFT)
32014             {
32015                 self->direction = DIRECTION_RIGHT;
32016             }
32017         }
32018     }
32019     if (self->ducking)
32020     {
32021         goto endthinkcheck;
32022     }
32023 
32024 
32025     //White Dragon: prepare for idling animations...
32026     if(action)
32027     {
32028         self->takeaction = NULL;
32029         self->idling = IDLING_PREPARED;
32030     }
32031 
32032     switch(action)
32033     {
32034     case 1:
32035         // back walk feature
32036         if(level && validanim(self, ANI_BACKWALK))
32037         {
32038             if(self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT)
32039             {
32040                 bkwalk = !self->direction;
32041             }
32042             else if(self->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_RIGHT)
32043             {
32044                 bkwalk = self->direction;
32045             }
32046             else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT) && self->direction == DIRECTION_LEFT)
32047             {
32048                 bkwalk = 1;
32049             }
32050             else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT) && self->direction == DIRECTION_RIGHT)
32051             {
32052                 bkwalk = 1;
32053             }
32054             else if(self->turntime && self->modeldata.turndelay)
32055             {
32056                 bkwalk = 1;
32057             }
32058             if(bkwalk)
32059             {
32060                 common_backwalk_anim(self);    //ent_set_anim(self, ANI_BACKWALK, 0);
32061             }
32062             else
32063             {
32064                 common_walk_anim(self);    //ent_set_anim(self, ANI_WALK, 0);    // If neither up nor down exist, set to walk
32065             }
32066         }
32067         else
32068         {
32069             common_walk_anim(self);    //ent_set_anim(self, ANI_WALK, 0);    // If neither up nor down exist, set to walk
32070         }
32071         break;
32072     case 2:
32073         common_up_anim(self); //ent_set_anim(self, ANI_UP, 0);    // Set to up animation if exists
32074         break;
32075     case 3:
32076         common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0);    // Set to down animation if exists
32077         break;
32078     case 4:
32079         if (validanim(self, ANI_BACKRUN))
32080         {
32081             if (is_in_backrun(self)) ent_set_anim(self, ANI_BACKRUN, 0);
32082             else ent_set_anim(self, ANI_RUN, 0);
32083         }
32084         else ent_set_anim(self, ANI_RUN, 0); // Set to run animation if exists
32085 
32086         break;
32087     default:
32088 
32089         if(self->idling)
32090         {
32091             common_idle_anim(self);
32092         }
32093         break;
32094     }
32095 
32096 
32097 endthinkcheck:
32098     //insert check here
32099     return;
32100 }
32101 
is_in_backrun(entity * self)32102 int is_in_backrun(entity* self)
32103 {
32104     if ( ((self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT) && self->velocity.x < 0) ||
32105          ((self->modeldata.facing == FACING_ADJUST_LEFT  || level->facing == FACING_ADJUST_LEFT)  && self->velocity.x > 0) ||
32106          (((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT)) && self->velocity.x > 0) ||
32107          (((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT))  && self->velocity.x > 0)
32108        ) return 1;
32109     else return 0;
32110 }
32111 
32112 //ammo count goes down
subtract_shot()32113 void subtract_shot()
32114 {
32115     if(self->weapent && self->weapent->modeldata.shootnum)
32116     {
32117         self->weapent->modeldata.shootnum--;
32118         if(!self->weapent->modeldata.shootnum)
32119         {
32120             self->weapent->modeldata.counter = 0;
32121             dropweapon(0);
32122         }
32123     }
32124 
32125 }
32126 
32127 
dropweapon(int flag)32128 void dropweapon(int flag)
32129 {
32130     int wall;
32131     entity *other = NULL;
32132 
32133 	// If we already have a weapon, we'll need to discard it.
32134     if(self->weapent)
32135     {
32136 		// 2019-09-29 - Not sure about this logic. It appears that only type shot
32137 		// weapons or weapons with shot ammo are dropped.  Anything else is simply discarded.
32138 		// Need to evaluate all weapon logic to get the workflow.
32139         if(self->weapent->modeldata.typeshot || (!self->weapent->modeldata.typeshot && self->weapent->modeldata.shootnum))
32140         {
32141 			// If the flag is 2 or below, we subtract the flag's
32142 			// value from weapon counter.
32143 			if(flag < 2)
32144             {
32145                 self->weapent->modeldata.counter -= flag;
32146             }
32147 
32148 			// We're going to use our own position for the weapon.
32149 			self->weapent->direction = self->direction;
32150 			self->weapent->position.z = self->position.z;
32151             self->weapent->position.x = self->position.x;
32152             self->weapent->position.y = self->position.y;
32153 
32154 			// Get any walls and platforms.
32155             other = check_platform(self->weapent->position.x, self->weapent->position.z, self);
32156             wall = checkwall_index(self->weapent->position.x, self->weapent->position.z);
32157 
32158 			// Place onto wall or platform.
32159             if(other && other != self->weapent)
32160             {
32161                 self->weapent->base += other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT];
32162             }
32163             else if(wall >= 0)
32164             {
32165                 self->weapent->base += level->walls[wall].height;
32166             }
32167 
32168 			// Use the weapon's RESPAWN or SPAWN animations if available, otherwise
32169 			// go right to idle.
32170             if(validanim(self->weapent, ANI_RESPAWN))
32171             {
32172                 ent_set_anim(self->weapent, ANI_RESPAWN, 1);
32173             }
32174             else if(validanim(self->weapent, ANI_SPAWN))
32175             {
32176                 ent_set_anim(self->weapent, ANI_SPAWN, 1);
32177             }
32178             else
32179             {
32180                 if(validanim(self->weapent, ANI_IDLE)) ent_set_anim(self->weapent, ANI_IDLE, 1);
32181             }
32182 
32183 			// If the weapon's counter is depleted, then weapon is lost for good.
32184 			// If it is an "animal", then we apply the animal running away logic.
32185 			// Otherwise the weapon blinks out.
32186             if(!self->weapent->modeldata.counter)
32187             {
32188                 if(!self->modeldata.animal)
32189                 {
32190                     self->weapent->blink = 1;
32191                     self->weapent->takeaction = common_lie;
32192                 }
32193                 else
32194                 {
32195                     self->weapent->modeldata.type = TYPE_NONE;
32196                     self->weapent->think = runanimal;
32197                 }
32198             }
32199             self->weapent->nextthink = _time + 1;
32200         }
32201 
32202 		// Clear our tracking variable that keeps the weapon entity pointer.
32203         self->weapent = NULL;
32204     }
32205 
32206 	// Flag 2 means we're probably setting the weapon directly (ex: setweapon command).
32207 	// In that case we don't worry about a weapon entity. Just switch ourselves over
32208 	// to the weapon model.
32209     if(flag < 2)
32210     {
32211         if(self->modeldata.type & TYPE_PLAYER)
32212         {
32213             if(player[self->playerindex].weapnum)
32214             {
32215                 set_weapon(self, player[self->playerindex].weapnum, 0);
32216             }
32217             else
32218             {
32219                 set_weapon(self, level->setweap, 0);
32220             }
32221         }
32222         else
32223         {
32224             set_weapon(self, 0, 0);
32225         }
32226     }
32227 
32228 	// Model override. If this is populated, we use its value
32229 	// to locate a model by index and revert to that instead
32230 	// of the default model when a weapon is lost.
32231     if(self->modeldata.weaploss[1] > 0)
32232     {
32233         set_weapon(self, self->modeldata.weaploss[1], 0);
32234     }
32235 }
32236 
32237 
player_takedamage(entity * other,s_collision_attack * attack,int fall_flag)32238 int player_takedamage(entity *other, s_collision_attack *attack, int fall_flag)
32239 {
32240     s_collision_attack atk = *attack;
32241     //printf("damaged by: '%s' %d\n", other->name, attack->attack_force);
32242     if(healthcheat || (level->nohurt == DAMAGE_FROM_ENEMY_OFF && (other->modeldata.type & TYPE_ENEMY)))
32243     {
32244         atk.attack_force = 0;
32245     }
32246 
32247     return common_takedamage(other, &atk, 0);
32248 }
32249 
32250 
32251 ////////////////////////////////
32252 
32253 // Called when player re-enters the game.
32254 // Drop all enemies EXCEPT for the linked/frozen ones.
drop_all_enemies()32255 void drop_all_enemies()
32256 {
32257     int i;
32258     entity *weapself = self;
32259     s_collision_attack attack;
32260 
32261     for(i = 0; i < ent_max; i++)
32262     {
32263         if(ent_list[i]->exists &&
32264                 ent_list[i]->energy_state.health_current > 0 &&
32265                 (ent_list[i]->modeldata.type & TYPE_ENEMY) &&
32266                 !ent_list[i]->owner &&    // Don't want to knock down a projectile
32267                 !ent_list[i]->frozen &&    // Don't want to unfreeze a frozen enemy
32268                 !ent_list[i]->modeldata.nomove &&
32269                 !ent_list[i]->modeldata.nodrop &&
32270                 validanim(ent_list[i], ANI_FALL) )
32271         {
32272             ent_list[i]->attacking = ATTACKING_NONE;
32273             ent_list[i]->projectile = BLAST_NONE;
32274             ent_list[i]->takeaction = common_fall;//enemy_fall;
32275             ent_list[i]->damage_on_landing.attack_force = 0;
32276             ent_list[i]->damage_on_landing.attack_type = ATK_NONE;
32277             self = ent_list[i];
32278             ent_unlink(self);
32279             ent_list[i]->velocity.x = (self->direction == DIRECTION_RIGHT) ? (-1.2) : 1.2;
32280             if(ent_list[i]->modeldata.weaploss[0] != WEAPLOSS_TYPE_CHANGE)
32281             {
32282                 dropweapon(1);
32283             }
32284             toss(ent_list[i], 2.5 + randf(1));
32285             ent_list[i]->knockdowncount = ent_list[i]->modeldata.knockdowncount;
32286 
32287             ent_list[i]->knockdowntime = 0;
32288 
32289             // Use default attack values.
32290             attack = emptyattack;
32291             set_fall(ent_list[i], self, &attack, 1);
32292         }
32293     }
32294     self = weapself;
32295 }
32296 
32297 
32298 
32299 // Called when boss dies
kill_all_enemies()32300 void kill_all_enemies()
32301 {
32302     int i;
32303     s_collision_attack attack;
32304     entity *tmpself = NULL;
32305 
32306     attack = emptyattack;
32307 	attack.attack_type = ATK_BOSS_DEATH;
32308 	attack.dropv = default_model_dropv;
32309 
32310     tmpself = self;
32311     for(i = 0; i < ent_max; i++)
32312     {
32313         if(  ent_list[i]->exists
32314                 && ent_list[i]->energy_state.health_current > 0
32315                 && (ent_list[i]->modeldata.type & TYPE_ENEMY)
32316                 && ent_list[i]->takedamage)
32317         {
32318             self = ent_list[i];
32319             attack.attack_force = self->energy_state.health_current;
32320             self->takedamage(tmpself, &attack, 0);
32321             self->dead = 1;
32322         }
32323     }
32324     self = tmpself;
32325 }
32326 
32327 
32328 
smart_bomb(entity * e,s_collision_attack * attack)32329 void smart_bomb(entity *e, s_collision_attack *attack)    // New method for smartbombs
32330 {
32331     int i, hostile, hit = 0;
32332     entity *tmpself = NULL;
32333 
32334     hostile = e->modeldata.hostile;
32335     if(e->modeldata.type & TYPE_PLAYER)
32336     {
32337         hostile &= ~(TYPE_PLAYER);
32338     }
32339 
32340     tmpself = self;
32341     for(i = 0; i < ent_max; i++)
32342     {
32343         if( ent_list[i]->exists
32344                 && ent_list[i] != e
32345                 && ent_list[i]->energy_state.health_current > 0
32346                 && (ent_list[i]->modeldata.type & (e->modeldata.hostile)))
32347         {
32348             self = ent_list[i];
32349             hit = 1; // for nocost, if the bomb doesn't hit, it won't cost energy
32350             if(self->takedamage)
32351             {
32352                 //attack.attack_drop = self->modeldata.knockdowncount+1;
32353                 self->takedamage(e, attack, 0);
32354             }
32355             else
32356             {
32357                 self->energy_state.health_current -= attack->attack_force;
32358                 if(self->energy_state.health_current <= 0)
32359                 {
32360                     kill_entity(self);
32361                 }
32362             }
32363         }
32364     }
32365     if(nocost && hit && smartbomber) // don't use e, because this can be an item-bomb
32366     {
32367         self = smartbomber;
32368 
32369         // Energycost defined?
32370         if(self->modeldata.animation[ANI_SPECIAL]->energycost)
32371         {
32372             if(check_energy(COST_CHECK_MP, ANI_SPECIAL))
32373             {
32374                 self->energy_state.mp_current -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
32375             }
32376             else
32377             {
32378                 self->energy_state.health_current -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
32379             }
32380         }
32381     }
32382     self = tmpself;
32383 
32384 }
32385 
32386 
32387 ////////////////////////////////
32388 
anything_walk()32389 void anything_walk()
32390 {
32391     if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80))
32392     {
32393         kill_entity(self);
32394         return;
32395     }
32396     //self->position.x += self->velocity.x;
32397 }
32398 
knife_spawn(char * name,int index,float x,float z,float a,int direction,int type,int map)32399 entity *knife_spawn(char *name, int index, float x, float z, float a, int direction, int type, int map)
32400 {
32401     entity *e = NULL;
32402 
32403     if(index >= 0 || name)
32404     {
32405         e = spawn(x, z, a, direction, name, index, NULL);
32406         if(!e)
32407         {
32408             return NULL;
32409         }
32410 
32411         // Index takes priority in spawning, so if it's here
32412         // then we'll type this as an index spawn source.
32413         if(index < 0)
32414         {
32415             e->projectile_prime |= PROJECTILE_PRIME_SOURCE_INDEX;
32416         }
32417         else
32418         {
32419             e->projectile_prime |= PROJECTILE_PRIME_SOURCE_NAME;
32420         }
32421 
32422         e->projectile_prime |= PROJECTILE_PRIME_BASE_Y;
32423         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING;
32424         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_UNDEFINED;
32425 
32426         e->position.y = a;
32427     }
32428     else if(self->weapent && self->weapent->modeldata.project >= 0)
32429     {
32430         e = spawn(x, z, a, direction, NULL, self->weapent->modeldata.project, NULL);
32431         if(!e)
32432         {
32433             return NULL;
32434         }
32435 
32436         e->projectile_prime |= PROJECTILE_PRIME_BASE_Y;
32437         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING;
32438         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_WEAPON;
32439         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_PROJECTILE;
32440 
32441         e->position.y = a;
32442     }
32443     else if(self->animation->projectile.knife >= 0)
32444     {
32445         e = spawn(x, z, a, direction, NULL, self->animation->projectile.knife, NULL);
32446         if(!e)
32447         {
32448             return NULL;
32449         }
32450 
32451         e->projectile_prime |= PROJECTILE_PRIME_BASE_Y;
32452         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING;
32453         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_ANIMATION;
32454         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_KNIFE;
32455 
32456         e->position.y = a;
32457     }
32458     else if(self->animation->projectile.flash >= 0)
32459     {
32460         e = spawn(x, z, 0, direction, NULL, self->animation->projectile.flash, NULL);
32461         if(!e)
32462         {
32463             return NULL;
32464         }
32465 
32466         e->projectile_prime |= PROJECTILE_PRIME_BASE_FLOOR;
32467         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_STATIONARY;
32468         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_ANIMATION;
32469         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_FLASH;
32470 
32471         e->position.y = 0;
32472     }
32473     else if(self->modeldata.knife >= 0)
32474     {
32475         e = spawn(x, z, a, direction, NULL, self->modeldata.knife, NULL);
32476         if(!e)
32477         {
32478             return NULL;
32479         }
32480 
32481         e->projectile_prime |= PROJECTILE_PRIME_BASE_Y;
32482         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING;
32483         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_HEADER;
32484         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_KNIFE;
32485 
32486         e->position.y = a;
32487     }
32488     else if(self->modeldata.pshotno >= 0)
32489     {
32490         e = spawn(x, z, 0, direction, NULL, self->modeldata.pshotno, NULL);
32491         if(!e)
32492         {
32493             return NULL;
32494         }
32495 
32496         e->projectile_prime |= PROJECTILE_PRIME_BASE_FLOOR;
32497         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_STATIONARY;
32498         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_HEADER;
32499         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_PSHOTNO;
32500 
32501         e->position.y = 0;
32502     }
32503     else if(type)
32504     {
32505         e = spawn(x, z, a, direction, "Shot", -1, NULL);
32506         if(!e)
32507         {
32508             return NULL;
32509         }
32510 
32511         e->projectile_prime |= PROJECTILE_PRIME_BASE_Y;
32512         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING;
32513         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_GLOBAL;
32514         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_SHOT;
32515 
32516         e->position.y = a;
32517     }
32518     else
32519     {
32520         e = spawn(x, z, a, direction, "Knife", -1, NULL);
32521         if(!e)
32522         {
32523             return NULL;
32524         }
32525 
32526         e->projectile_prime |= PROJECTILE_PRIME_BASE_Y;
32527         e->projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING;
32528         e->projectile_prime |= PROJECTILE_PRIME_SOURCE_GLOBAL;
32529         e->projectile_prime |= PROJECTILE_PRIME_REQUEST_KNIFE;
32530 
32531         e->position.y = a;
32532     }
32533 
32534     if(e == NULL)
32535     {
32536         return NULL;
32537     }
32538     else if(self->modeldata.type & TYPE_PLAYER)
32539     {
32540         e->modeldata.type = TYPE_SHOT;
32541     }
32542     else
32543     {
32544         e->modeldata.type = self->modeldata.type;
32545     }
32546 
32547     if(!e->model->speed && !e->modeldata.nomove)
32548     {
32549         e->modeldata.speed = 2;
32550     }
32551     else if(e->modeldata.nomove)
32552     {
32553         e->modeldata.speed = 0;
32554     }
32555 
32556     e->spawntype = SPAWN_TYPE_PROJECTILE_NORMAL;
32557     e->owner = self;                                                     // Added so projectiles don't hit the owner
32558     e->nograb = 1;                                                       // Prevents trying to grab a projectile
32559     e->attacking = ATTACKING_ACTIVE;
32560     //e->direction = direction;
32561     e->think = common_think;
32562     e->nextthink = _time + 1;
32563     e->trymove = NULL;
32564     e->takedamage = arrow_takedamage;
32565     e->takeaction = NULL;
32566     e->modeldata.aimove = AIMOVE1_ARROW;
32567     if(!e->modeldata.offscreenkill)
32568     {
32569         e->modeldata.offscreenkill = 200;    //default value
32570     }
32571     e->modeldata.aiattack = AIATTACK1_NOATTACK;
32572 
32573 	// Kill self when we hit.
32574 	if (e->modeldata.remove)
32575 	{
32576 		e->autokill |= AUTOKILL_ATTACK_HIT;
32577 	}
32578 
32579     // Kill self when we finish animation.
32580 	if (e->modeldata.nomove)
32581 	{
32582 		e->autokill |= AUTOKILL_ANIMATION_COMPLETE;
32583 	}
32584 
32585     e->speedmul = 2;
32586 
32587     ent_set_colourmap(e, map);
32588 
32589     if(e->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR)
32590     {
32591         e->base = 0;
32592     }
32593     else
32594     {
32595         e->base = a;
32596     }
32597 
32598     if(e->modeldata.hostile < 0)
32599     {
32600         e->modeldata.hostile = self->modeldata.hostile;
32601     }
32602     if(e->modeldata.candamage < 0)
32603     {
32604         e->modeldata.candamage = self->modeldata.candamage;
32605     }
32606     if((self->modeldata.type & TYPE_PLAYER) && ((level && level->nohit == DAMAGE_FROM_PLAYER_OFF) || savedata.mode))
32607     {
32608         e->modeldata.hostile &= ~TYPE_PLAYER;
32609         e->modeldata.candamage &= ~TYPE_PLAYER;
32610     }
32611 
32612     e->modeldata.subject_to_wall = e->modeldata.subject_to_platform = e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
32613     e->modeldata.no_adjust_base  = 1;
32614 
32615 	// Execute the projectile's on spawn event.
32616 	execute_onspawn_script(e);
32617 
32618 	return e;
32619 }
32620 
bomb_explode()32621 void bomb_explode()
32622 {
32623     if(self->animating)
32624     {
32625         return;
32626     }
32627     kill_entity(self);
32628 }
32629 
32630 
bomb_spawn(char * name,int index,float x,float z,float a,int direction,int map)32631 entity *bomb_spawn(char *name, int index, float x, float z, float a, int direction, int map)
32632 {
32633     entity *e = NULL;
32634 
32635     if(index >= 0 || name)
32636     {
32637         e = spawn(x, z, a, direction, name, index, NULL);
32638     }
32639     else if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && self->weapent->modeldata.project >= 0)
32640     {
32641         e = spawn(x, z, a, direction, NULL, self->weapent->modeldata.project, NULL);
32642     }
32643     else if(self->animation->projectile.bomb >= 0)
32644     {
32645         e = spawn(x, z, a, direction, NULL, self->animation->projectile.bomb, NULL);
32646     }
32647     else if(self->modeldata.bomb >= 0)
32648     {
32649         e = spawn(x, z, a, direction, NULL, self->modeldata.bomb, NULL);
32650     }
32651 
32652     if(e == NULL)
32653     {
32654         return NULL;
32655     }
32656 
32657     e->position.y = a + 0.5;
32658 
32659     if(!e->model->speed && !e->modeldata.nomove)
32660     {
32661         e->modeldata.speed = 2;
32662     }
32663     else if(e->modeldata.nomove)
32664     {
32665         e->modeldata.speed = 0;
32666     }
32667 
32668     e->spawntype = SPAWN_TYPE_PROJECTILE_BOMB;
32669     e->attacking = ATTACKING_ACTIVE;
32670     e->owner = self;                                                     // Added so projectiles don't hit the owner
32671     e->nograb = 1;                                                       // Prevents trying to grab a projectile
32672     e->toexplode |= EXPLODE_PREPARED;                                    // Set to distinguish exploding projectiles and also so stops falling when hitting an opponent
32673     ent_set_colourmap(e, map);
32674     //e->direction = direction;
32675     toss(e, e->modeldata.jumpheight);
32676     e->think = common_think;
32677     e->nextthink = _time + 1;
32678     e->trymove = NULL;
32679     e->takeaction = NULL;
32680     e->modeldata.aimove = AIMOVE1_BOMB;
32681     e->modeldata.aiattack = AIATTACK1_NOATTACK;                                    // Well, bomb's attack animation is passive, dont use any A.I. code.
32682     e->takedamage = common_takedamage;
32683 	e->autokill &= ~AUTOKILL_ATTACK_HIT;
32684 
32685 	if (e->modeldata.nomove)
32686 	{
32687 		e->autokill |= AUTOKILL_ANIMATION_COMPLETE;
32688 	}
32689 
32690     e->speedmul = 2;
32691 
32692 
32693     // Ok, some old mods use type none, will have troubles.
32694     // so we give them some default hostile types.
32695     if(e->modeldata.hostile < 0)
32696     {
32697         e->modeldata.hostile = self->modeldata.hostile;
32698     }
32699     if(e->modeldata.candamage < 0)
32700     {
32701         e->modeldata.candamage = self->modeldata.candamage;
32702     }
32703     e->modeldata.no_adjust_base = 0;
32704     e->modeldata.subject_to_basemap = e->modeldata.subject_to_wall = e->modeldata.subject_to_platform = e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
32705 
32706 	// Execute the projectile's on spawn event.
32707 	execute_onspawn_script(e);
32708 
32709 	return e;
32710 }
32711 
32712 // Spawn 3 stars
star_spawn(float x,float z,float a,int direction)32713 int star_spawn(float x, float z, float a, int direction)  // added entity to know which star to load
32714 {
32715     entity *e = NULL;
32716     int i, index = -1;
32717     char *starname = NULL;
32718     float fd = (float)((direction ? 2 : -2));
32719     int max_stars = 3;
32720     int first_sortid = 0;
32721 
32722     //merge enemy/player together, use the same rules
32723     if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && self->weapent->modeldata.project >= 0)
32724     {
32725         index = self->weapent->modeldata.project;
32726     }
32727     else if(self->animation->projectile.star >= 0)
32728     {
32729         index = self->animation->projectile.star;    //use any star
32730     }
32731     else if(self->modeldata.star >= 0)
32732     {
32733         index = self->modeldata.star;
32734     }
32735     else
32736     {
32737         starname = "Star";    // this is default star
32738     }
32739 
32740     for(i = 0; i < max_stars; i++)
32741     {
32742         e = spawn(x, z, a, direction, starname, index, NULL);
32743         if(e == NULL)
32744         {
32745             return 0;
32746         }
32747 
32748         self->attacking = ATTACKING_NONE;
32749 
32750         if (i <= 0) first_sortid = e->sortid;
32751         e->sortid = first_sortid - i;
32752         e->takedamage = arrow_takedamage;//enemy_takedamage;    // Players can now hit projectiles
32753         e->owner = self;    // Added so enemy projectiles don't hit the owner
32754         e->attacking = ATTACKING_ACTIVE;
32755         e->nograb = 1;    // Prevents trying to grab a projectile
32756         if (self->animation->starvelocity)
32757         {
32758             e->velocity.x = fd * (float)self->animation->starvelocity[i];
32759         }
32760         else e->velocity.x = fd * (float)i / 2;
32761         e->think = common_think;
32762         e->nextthink = _time + 1;
32763         e->trymove = NULL;
32764         e->takeaction = NULL;
32765         e->modeldata.aimove = AIMOVE1_STAR;
32766         e->modeldata.aiattack = AIATTACK1_NOATTACK;
32767 
32768 		if (e->modeldata.remove)
32769 		{
32770 			e->autokill |= AUTOKILL_ATTACK_HIT;
32771 		}
32772 
32773 		e->position.y = e->base = a;
32774         e->speedmul = 2;
32775         //e->direction = direction;
32776 
32777         if(e->modeldata.hostile < 0)
32778         {
32779             e->modeldata.hostile = self->modeldata.hostile;
32780         }
32781         if(e->modeldata.candamage < 0)
32782         {
32783             e->modeldata.candamage = self->modeldata.candamage;
32784         }
32785 
32786         e->modeldata.subject_to_basemap = e->modeldata.subject_to_wall = e->modeldata.subject_to_platform =
32787                                            e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
32788         e->modeldata.no_adjust_base = 0;
32789 
32790         e->spawntype = SPAWN_TYPE_PROJECTILE_STAR;
32791 
32792 		// Execute the projectile's on spawn event.
32793 		execute_onspawn_script(e);
32794     }
32795     return 1;
32796 }
32797 
32798 
32799 
steam_think()32800 void steam_think()
32801 {
32802     if(!self->animating)
32803     {
32804         kill_entity(self);
32805         return;
32806     }
32807 
32808     self->base += 1;
32809     self->position.y = self->base;
32810 }
32811 
32812 
32813 
32814 // for the "trap" type   7-1-2005  trap start
trap_think()32815 void trap_think()
32816 {
32817     self->attacking = ATTACKING_ACTIVE;
32818 }
32819 //    7-1-2005  trap end
32820 
32821 
32822 
32823 
steam_spawn(float x,float z,float a)32824 void steam_spawn(float x, float z, float a)
32825 {
32826     entity *e = NULL;
32827 
32828     e = spawn(x, z, a, 0, "Steam", -1, NULL);
32829 
32830     if(e == NULL)
32831     {
32832         return;
32833     }
32834 
32835     e->spawntype = SPAWN_TYPE_STEAM;
32836     e->base = a;
32837     e->modeldata.no_adjust_base = 1;
32838     e->think = steam_think;
32839 
32840 	// Execute the steams's on spawn event.
32841 	execute_onspawn_script(e);
32842 }
32843 
32844 
32845 
steamer_think()32846 void steamer_think()
32847 {
32848     steam_spawn(self->position.x, self->position.z, self->position.y);
32849     self->nextthink = _time + (GAME_SPEED / 10) + (rand32() & 31);
32850 }
32851 
32852 
32853 
text_think()32854 void text_think()     // New function so text can be displayed
32855 {
32856     // wait to suicide
32857     if(!self->animating)
32858     {
32859         kill_entity(self);
32860     }
32861 }
32862 
32863 ////////////////////////////////
32864 
32865 //homing arrow find its target
32866 // type : target type
homing_find_target(int type)32867 entity *homing_find_target(int type)
32868 {
32869     int i, min, max;
32870     int index = -1;
32871     //use the walk animation's range
32872     if(validanim(self, ANI_WALK))
32873     {
32874         min = self->modeldata.animation[ANI_WALK]->range.x.min;
32875         max = self->modeldata.animation[ANI_WALK]->range.x.max;
32876     }
32877     else
32878     {
32879         min = 0;
32880         max = 999;
32881     }
32882     //find the 'nearest' one
32883     for(i = 0; i < ent_max; i++)
32884     {
32885         if( ent_list[i]->exists && ent_list[i] != self //cant target self
32886                 && (ent_list[i]->modeldata.type & type)
32887                 && diff(ent_list[i]->position.x, self->position.x) + diff(ent_list[i]->position.z, self->position.z) >= min
32888                 && diff(ent_list[i]->position.x, self->position.x) + diff(ent_list[i]->position.z, self->position.z) <= max
32889                 && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]  )
32890         {
32891             if(index < 0 || diff(ent_list[i]->position.x, self->position.x) + diff(ent_list[i]->position.z, self->position.z) < diff(ent_list[index]->position.x, self->position.x) + diff(ent_list[index]->position.z, self->position.z))
32892             {
32893                 index = i;
32894             }
32895         }
32896     }
32897     if( index >= 0)
32898     {
32899         return ent_list[index];
32900     }
32901     return NULL;
32902 }
32903 
32904 
bike_crash()32905 void bike_crash()
32906 {
32907     int i;
32908     if(self->direction == DIRECTION_RIGHT)
32909     {
32910         self->velocity.x = 2;
32911     }
32912     else
32913     {
32914         self->velocity.x = -2;
32915     }
32916     for(i = 0; i < levelsets[current_set].maxplayers; i++)
32917     {
32918         if (savedata.joyrumble[i]) control_rumble(i, 1, 100);
32919     }
32920     //if(self->position.x < advancex-100 || self->position.x > advancex+(videomodes.hRes+100)) kill_entity(self);
32921 }
32922 
32923 
32924 
biker_takedamage(entity * other,s_collision_attack * attack,int fall_flag)32925 int biker_takedamage(entity *other, s_collision_attack *attack, int fall_flag)
32926 {
32927     entity *driver = NULL;
32928     entity *tempself = NULL;
32929     if(self->dead)
32930     {
32931         return 0;
32932     }
32933     // Fell in a hole
32934     if(self->position.y < PIT_DEPTH)
32935     {
32936         kill_entity(self);
32937         return 0;
32938     }
32939     if(other != self)
32940     {
32941         set_opponent(other, self);
32942     }
32943 
32944     if(attack->no_pain) // don't drop driver until it is dead, because the attack has no pain effect
32945     {
32946         checkdamage(other, attack);
32947         if(self->energy_state.health_current > 0)
32948         {
32949             return 1;    // not dead yet
32950         }
32951     }
32952 
32953     check_backpain(other,self);
32954     set_pain(self,  self->last_damage_type, 1);
32955     self->attacking = ATTACKING_ACTIVE;
32956     if(!self->modeldata.offscreenkill)
32957     {
32958         self->modeldata.offscreenkill = 100;
32959     }
32960     self->think = bike_crash;
32961     // well, this is the real entity, the driver who take the damage
32962     if((driver = drop_driver(self)))
32963     {
32964         driver->position.y = self->position.y;
32965         tempself = self;
32966         self = driver;
32967         self->drop = 1;
32968         self->direction = tempself->direction;
32969         if(self->takedamage)
32970         {
32971             self->takedamage(other, attack, 0);
32972         }
32973         else
32974         {
32975             self->energy_state.health_current -= attack->attack_force;
32976         }
32977         self = tempself;
32978 
32979     }
32980     self->energy_state.health_current = 0;
32981     checkdeath();
32982     return 1;
32983 }
32984 
32985 
32986 
obstacle_fall()32987 void obstacle_fall()
32988 {
32989     if(inair(self))
32990     {
32991         return;
32992     }
32993 
32994     self->velocity.x = self->velocity.z = 0;
32995     if((!self->animating && validanim(self, ANI_DIE)) || !validanim(self, ANI_DIE))
32996     {
32997         kill_entity(self);    // Fixed so ANI_DIE can be used
32998     }
32999 }
33000 
33001 
33002 
obstacle_fly()33003 void obstacle_fly()    // Now obstacles can fly when hit like on Simpsons/TMNT
33004 {
33005     //self->position.x += self->velocity.x * 4;    // Equivelant of speed 40
33006     if(self->position.x > advancex + (videomodes.hRes + 200) || self->position.x < advancex - 200)
33007     {
33008         kill_entity(self);
33009     }
33010 }
33011 
33012 
33013 
obstacle_takedamage(entity * other,s_collision_attack * attack,int fall_flag)33014 int obstacle_takedamage(entity *other, s_collision_attack *attack, int fall_flag)
33015 {
33016     if(self->position.y <= PIT_DEPTH)
33017     {
33018         kill_entity(self);
33019         return 0;
33020     }
33021 
33022     //self->next_hit_time = _time + (attack->next_hit_time?attack->next_hit_time:(GAME_SPEED / 5));
33023     set_opponent(other, self);
33024     if(self->opponent && (self->opponent->modeldata.type & TYPE_PLAYER))
33025     {
33026         if (savedata.joyrumble[self->opponent->playerindex]) control_rumble(self->opponent->playerindex, 1, 75);
33027     }
33028     checkdamage(other, attack);
33029     self->playerindex = other->playerindex;    // Added so points go to the correct player
33030     addscore(other->playerindex, attack->attack_force * self->modeldata.multiple);  // Points can now be given for hitting an obstacle
33031 
33032     if(self->energy_state.health_current <= 0)
33033     {
33034 
33035         checkdeath();
33036 
33037         if(other->position.x < self->position.x)
33038         {
33039             self->velocity.x = 1;
33040         }
33041         else
33042         {
33043             self->velocity.x = -1;
33044         }
33045 
33046         self->attacking = ATTACKING_ACTIVE;    // So obstacles can explode and hurt players/enemies
33047 
33048         if(self->modeldata.subtype == SUBTYPE_FLYDIE)     // Now obstacles can fly like on Simpsons/TMNT
33049         {
33050             self->velocity.x *= 4;
33051             self->think = obstacle_fly;
33052             ent_set_anim(self, ANI_FALL, 0);
33053         }
33054         else
33055         {
33056             self->think = obstacle_fall;
33057 
33058             if(validanim(self, ANI_DIE))
33059             {
33060                 ent_set_anim(self, ANI_DIE, 0);    //  LTB 1-13-05  Die before toss
33061             }
33062             else
33063             {
33064                 toss(self, self->modeldata.jumpheight / 1.333);
33065                 ent_set_anim(self, ANI_FALL, 0);
33066             }
33067 
33068             if(!self->modeldata.nodieblink)
33069             {
33070                 self->blink = 1;
33071             }
33072         }
33073     }
33074 
33075     self->nextthink = _time + 1;
33076     return 1;
33077 }
33078 
33079 // Caskey, Damon V.
33080 // 2018-04-27
33081 //
33082 // Allocate memory and set entity properties that will be transfered
33083 // to a dropped item.
initialize_item_carry(entity * ent,s_spawn_entry * spawn_entry)33084 void initialize_item_carry(entity *ent, s_spawn_entry *spawn_entry)
33085 {
33086     // It's possible to call this from script, so if
33087     // if there is already memory for an item allocated
33088     // here, clear it out to make sure we don't end up
33089     // with any memory leaks.
33090     if(ent->item_properties)
33091     {
33092        free(ent->item_properties);
33093        ent->item_properties = NULL;
33094     }
33095 
33096     // Allocate memory for the item.
33097     ent->item_properties = malloc(sizeof(*ent->item_properties));
33098     memset(ent->item_properties, 0, sizeof(*ent->item_properties));
33099 
33100     if(spawn_entry)
33101     {
33102         ent->item_properties->index = spawn_entry->item_properties.index;
33103 
33104         if(spawn_entry->item_properties.alias[0])
33105         {
33106             strcpy(ent->item_properties->alias, spawn_entry->item_properties.alias);
33107         }
33108 
33109         if(spawn_entry->item_properties.colorset)
33110         {
33111             ent->item_properties->colorset = spawn_entry->item_properties.colorset;
33112         }
33113 
33114         if(spawn_entry->item_properties.health)
33115         {
33116             ent->item_properties->health = spawn_entry->item_properties.health;
33117         }
33118         ent->item_properties->player_count = spawn_entry->item_properties.player_count;
33119     }
33120 }
33121 
smartspawn(s_spawn_entry * props)33122 entity *smartspawn(s_spawn_entry *props)      // 7-1-2005 Entire section replaced with lord balls code
33123 {
33124     entity *e = NULL;
33125     entity *wp = NULL;
33126     int playercount;
33127 
33128     if( props == NULL /*||
33129         (level == NULL &&
33130          (!selectScreen && !titleScreen && !hallOfFame && !gameOver && !showComplete && !currentScene && !enginecreditsScreen && !menuScreen && !startgameMenu && !newgameMenu && !loadgameMenu &&
33131           !optionsMenu && !controloptionsMenu && !soundoptionsMenu && !videooptionsMenu && !systemoptionsMenu)
33132         )*/
33133       )
33134     {
33135         return NULL;
33136     }
33137 
33138     // Now you can make it so enemies/obstacles/etc only spawn if there are 2 players
33139     if(props->spawnplayer_count >= (playercount = MAX(1, count_ents(TYPE_PLAYER))))
33140     {
33141         if(props->boss && level != NULL)
33142         {
33143             --level->bossescount;
33144         }
33145         return NULL;
33146     }
33147 
33148     if( level != NULL && ((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD)) )
33149     {
33150         e = spawn(props->position.x, props->position.z + advancey, props->position.y, props->flip, props->name, props->index, props->model);
33151     }
33152     else
33153     {
33154         e = spawn(props->position.x + advancex, props->position.z, props->position.y, props->flip, props->name, props->index, props->model);
33155     }
33156 
33157     if(e == NULL)
33158     {
33159         return NULL;
33160     }
33161 
33162     //printf("%s, (%f, %f, %f) - (%f, %f, %f)", props->name, props->position.x, props->position.z, props->position.y, e->position.x, e->position.z, e->position.y);
33163 
33164     // Alias?
33165     if(props->alias[0])
33166     {
33167         memset(e->name, 0, sizeof(e->name));
33168         strcpy(e->name, props->alias);
33169     }
33170 
33171     // If we have item properties in spawn entry, then prepare a set of
33172     // properties to pass on to the item when it is dropped.
33173     if(props->item)
33174     {
33175         initialize_item_carry(e, props);
33176     }
33177 
33178     if(props->spawntype)
33179     {
33180         e->spawntype = props->spawntype;    //2011_03_23, Pass spawntype.
33181     }
33182 
33183     if(props->health[playercount - 1] != 0)
33184     {
33185         e->energy_state.health_current = e->modeldata.health = props->health[playercount - 1];
33186     }
33187 
33188     if(props->mp != 0)
33189     {
33190         e->energy_state.mp_current = e->modeldata.mp = props->mp;
33191     }
33192 
33193     if(props->score != 0)
33194     {
33195         e->modeldata.score = props->score;    // Overwrite score if exists in the level's. txt file
33196     }
33197     if(props->multiple != 0)
33198     {
33199         e->modeldata.multiple = props->multiple;    // Overwrite multiple if exists in the level's .txt file
33200     }
33201 
33202     if(!e->map && props->colourmap)
33203     {
33204         ent_set_colourmap(e, props->colourmap);
33205     }
33206 
33207     if(props->aggression)
33208     {
33209         e->modeldata.aggression = props->aggression;    // Aggression can be changed with spawn points now
33210     }
33211     if(props->item_properties.alpha)
33212     {
33213         e->item_properties->alpha = props->item_properties.alpha;
33214     }
33215     if(props->alpha)
33216     {
33217         e->modeldata.alpha = props->alpha;
33218     }
33219 
33220     // Feb 26, 2005 - Store the original map to be able to restore with dying flash
33221     if(props->dying)
33222     {
33223         e->dying = props->dying;    // Feb 26, 2005 - Used to define which colourmap is used for the dying flash
33224         e->per1 = props->per1;    // Mar 21, 2005 - Used to store custom percentages
33225         e->per2 = props->per2;    // Mar 21, 2005 - Used to store custom percentages
33226         e->dying2 = props->dying2;    // Dec 15, 2016 - Used to define which colourmap is used for the dying flash for per2 By White Dragon
33227     }
33228 
33229     if(props->nolife)
33230     {
33231         e->modeldata.nolife = props->nolife;    // Overwrite whether live is visible or not
33232     }
33233     e->boss = props->boss;
33234 
33235     if(props->boss && level != NULL && level->bossmusic[0])
33236     {
33237         music(level->bossmusic, 1, level->bossmusic_offset);
33238     }
33239 
33240     // give the entity a weapon item
33241     if(props->weapon)
33242     {
33243         wp = spawn(e->position.x, ITEM_HIDE_POSITION_Z, 0, 0, props->weapon, props->weaponindex, props->weaponmodel);
33244         if(wp)
33245         {
33246             //ent_default_init(wp);
33247             set_weapon(e, wp->modeldata.weapnum, 0);
33248             e->weapent = wp;
33249 
33250             e->weapent->spawntype = SPAWN_TYPE_WEAPON;
33251         }
33252     }
33253 
33254     // set entity type: player, enemy, npc...
33255     if(props->entitytype)
33256     {
33257         e->modeldata.type = props->entitytype;
33258     }
33259 
33260     // set a parent
33261     if(props->parent) //->varlist->vars->vt != VT_EMPTY
33262     {
33263         e->parent = (entity *)props->parent;
33264     }
33265 
33266     //ent_default_init(e);
33267     execute_onspawn_script(e);
33268     execute_spawn_script(props, e);
33269     return e;
33270 }   // 7-1-2005 replaced section ends here
33271 
is_incam(float x,float z,float a,float threshold)33272 int is_incam(float x, float z, float a, float threshold)
33273 {
33274     if (level)
33275     {
33276         if ( x >= advancex+threshold && x <= advancex+videomodes.hRes-threshold && z-a >= advancey+threshold && z-a <= advancey+videomodes.vRes-4 ) {
33277             if ( x >= scrollminx && x <= scrollmaxx+videomodes.hRes && z >= PLAYER_MIN_Z && z <= PLAYER_MAX_Z ) {
33278                 return 1;
33279             }
33280         }
33281     }
33282 
33283     return 0;
33284 }
33285 
spawnplayer(int index)33286 void spawnplayer(int index)
33287 {
33288     s_spawn_entry p;
33289     //s_model * model = NULL;
33290     int wall;
33291     int xc, zc, find = 0;
33292     index &= 3;
33293 
33294 //    model = find_model(player[index].name);
33295 //    if(model == NULL) return;
33296 
33297     memset(&p, 0, sizeof(p));
33298     p.name = player[index].name;
33299     p.index = -1;
33300     p.item_properties.index = -1;
33301     p.weaponindex = -1;
33302     p.colourmap = player[index].colourmap;
33303     p.spawnplayer_count = -1;
33304 
33305     if(level->scrolldir & SCROLL_LEFT)
33306     {
33307         if(level->spawn && level->spawn[index].x)
33308         {
33309             p.position.x = (float)(videomodes.hRes - level->spawn[index].x);
33310         }
33311         else
33312         {
33313             p.position.x = (float)((videomodes.hRes - 20) - 30 * index);
33314         }
33315     }
33316     else
33317     {
33318         if(level->spawn && level->spawn[index].x)
33319         {
33320             p.position.x = (float)(level->spawn[index].x);
33321         }
33322         else
33323         {
33324             p.position.x = (float)(20 + 30 * index);
33325         }
33326         p.flip = 1;
33327     }
33328 
33329     if(level->spawn && level->spawn[index].z)
33330     {
33331         if(level->scrolldir & (SCROLL_INWARD | SCROLL_OUTWARD))
33332         {
33333             p.position.z = (float)(level->spawn[index].z);
33334         }
33335         else
33336         {
33337             p.position.z = (float)(PLAYER_MIN_Z + level->spawn[index].z);
33338         }
33339     }
33340     else if(PLAYER_MAX_Z - PLAYER_MIN_Z > 5)
33341     {
33342         p.position.z = (float)(PLAYER_MIN_Z + 5);
33343     }
33344     else
33345     {
33346         p.position.z = (float)PLAYER_MIN_Z;
33347     }
33348 
33349     if(p.position.z < PLAYER_MIN_Z)
33350     {
33351         p.position.z = PLAYER_MIN_Z;
33352     }
33353     else if(p.position.z > PLAYER_MAX_Z)
33354     {
33355         p.position.z = PLAYER_MAX_Z;
33356     }
33357 
33358     //////////////////checking holes/ walls///////////////////////////////////
33359     for(xc = 0; xc < videomodes.hRes / 4; xc++)
33360     {
33361         if(p.position.x > videomodes.hRes)
33362         {
33363             p.position.x -= videomodes.hRes;
33364         }
33365         if(p.position.x < 0)
33366         {
33367             p.position.x += videomodes.hRes;
33368         }
33369         if(PLAYER_MIN_Z == PLAYER_MAX_Z)
33370         {
33371             wall = checkwall_index(advancex + p.position.x, p.position.z);
33372             if(wall >= 0 && level->walls[wall].height < MAX_WALL_HEIGHT)
33373             {
33374                 break;    //found
33375             }
33376             if(checkhole_in(advancex + p.position.x, p.position.z, p.position.y) || (wall >= 0 && level->walls[wall].height >= MAX_WALL_HEIGHT))
33377             {
33378                 find = 0;
33379             }
33380             else
33381             {
33382                 break;    // found
33383             }
33384         }
33385         else for(zc = 0; zc < (PLAYER_MAX_Z - PLAYER_MIN_Z) / 3; zc++, p.position.z += 3)
33386             {
33387                 if(p.position.z > PLAYER_MAX_Z)
33388                 {
33389                     p.position.z -= PLAYER_MAX_Z - PLAYER_MIN_Z;
33390                 }
33391                 if(p.position.z < PLAYER_MIN_Z)
33392                 {
33393                     p.position.z += PLAYER_MAX_Z - PLAYER_MIN_Z;
33394                 }
33395                 wall = checkwall_index(advancex + p.position.x, p.position.z);
33396                 if(wall >= 0 && level->walls[wall].height < MAX_WALL_HEIGHT)
33397                 {
33398                     find = 1;
33399                     break;
33400                 }
33401                 else if(wall >= 0 && level->walls[wall].height >= MAX_WALL_HEIGHT)
33402                 {
33403                     continue;
33404                 }
33405                 if(checkhole_in(advancex + p.position.x, p.position.z, p.position.y))
33406                 {
33407                     continue;
33408                 }
33409                 find = 1;
33410                 break;
33411             }
33412         if(find)
33413         {
33414             break;
33415         }
33416         p.position.x += (level->scrolldir & SCROLL_LEFT) ? -4 : 4;
33417     }
33418     ///////////////////////////////////////////////////////////////////////
33419     currentspawnplayer = index;
33420     player[index].ent = smartspawn(&p);
33421 
33422     if(player[index].ent == NULL)
33423     {
33424         borShutdown(1, "Fatal: unable to spawn player from '%s'\n", p.name);
33425     }
33426 
33427     player[index].ent->playerindex = index;
33428     player[index].ent->spawntype = SPAWN_TYPE_PLAYER_MAIN;
33429     if(nomaxrushreset[4] >= 1)
33430     {
33431         player[index].ent->rush.count.max = nomaxrushreset[index];
33432     }
33433     else
33434     {
33435         player[index].ent->rush.count.max = 0;
33436     }
33437 
33438     memset(player[index].combokey, 0, sizeof(*player[index].combokey)*MAX_SPECIAL_INPUTS);
33439     memset(player[index].inputtime, 0, sizeof(*player[index].inputtime)*MAX_SPECIAL_INPUTS);
33440     player[index].combostep = 0;
33441 
33442     if(player[index].spawnhealth)
33443     {
33444         player[index].ent->energy_state.health_current = player[index].spawnhealth + 5;
33445     }
33446     if(player[index].ent->energy_state.health_current > player[index].ent->modeldata.health)
33447     {
33448         player[index].ent->energy_state.health_current = player[index].ent->modeldata.health;
33449     }
33450 
33451     //mp little recorver after a level by tails
33452     if(player[index].spawnmp)
33453     {
33454         player[index].ent->energy_state.mp_current = player[index].spawnmp + 2;
33455     }
33456     if(player[index].ent->energy_state.mp_current > player[index].ent->modeldata.mp)
33457     {
33458         player[index].ent->energy_state.mp_current = player[index].ent->modeldata.mp;
33459     }
33460 
33461     if(player[index].weapnum)
33462     {
33463         set_weapon(player[index].ent, player[index].weapnum, 0);
33464     }
33465     else
33466     {
33467         set_weapon(player[index].ent, level->setweap, 0);
33468     }
33469 }
33470 
no_player_alive_to_join()33471 int no_player_alive_to_join()
33472 {
33473     int no_alive_players = 0;
33474     int i;
33475     for(i = 0; i < MAX_PLAYERS; i++)
33476     {
33477         if( ((!player[i].ent || player[i].lives <= 0 || (player[i].lives <= 1 && player[i].ent->energy_state.health_current <= 0)) &&
33478             ((noshare && player[i].credits <= 0) || (!noshare && credits <= 0)))
33479         )
33480         {
33481             ++no_alive_players;
33482         }
33483     }
33484     no_alive_players = (no_alive_players >= MAX_PLAYERS) ? 1 : 0;
33485 
33486     return no_alive_players;
33487 }
33488 
kill_all_players_by_timeover()33489 void kill_all_players_by_timeover()
33490 {
33491     int i;
33492     s_collision_attack attack_timeover, attack_lose;
33493 
33494     attack_timeover = emptyattack;
33495     attack_timeover.attack_type = ATK_TIMEOVER;
33496     attack_timeover.dropv.y = default_model_dropv.y;
33497     attack_timeover.dropv.x = default_model_dropv.x;
33498     attack_timeover.dropv.z = default_model_dropv.z;
33499 
33500     attack_lose = emptyattack;
33501     attack_lose.attack_type = ATK_LOSE;
33502 
33503     endgame = 1;
33504     for(i = 0; i < MAX_PLAYERS; i++)
33505     {
33506         entity* tmp = self;
33507         self = player[i].ent;
33508         if(self && !validanim(self, ANI_LOSE))
33509         {
33510             endgame = 0;
33511             attack_timeover.attack_force = self->energy_state.health_current;
33512             self->takedamage(self, &attack_timeover, 0);
33513         }
33514         else if(self)
33515         {
33516             endgame = 0;
33517 
33518             attack_lose.attack_force = self->energy_state.health_current;
33519             if (inair(self) && validanim(self, ANI_FALLLOSE))
33520             {
33521                 attack_lose.dropv.y = default_model_dropv.y;
33522                 attack_lose.dropv.x = default_model_dropv.x;
33523                 attack_lose.dropv.z = default_model_dropv.z;
33524                 self->modeldata.falldie = 2;
33525             }
33526             else
33527             {
33528                 self->modeldata.falldie = 1;
33529             }
33530             self->takedamage(self, &attack_lose, 0);
33531         }
33532         self = tmp;
33533     }
33534 }
33535 
time_over()33536 void time_over()
33537 {
33538     if(level->type == 1)
33539     {
33540         level_completed = 1;    // Feb 25, 2005 - Used for bonus levels so a life isn't taken away if time expires.level->type == 1 means bonus level, else regular
33541     }
33542     else if(!level_completed)
33543     {
33544         kill_all_players_by_timeover();
33545 
33546         if (!is_total_timeover)
33547         {
33548             if(SAMPLE_TIMEOVER >= 0)
33549             {
33550                 sound_play_sample(SAMPLE_TIMEOVER, 0, savedata.effectvol, savedata.effectvol, 100);
33551             }
33552 
33553             timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
33554             if(!endgame)
33555             {
33556                 showtimeover = 1;
33557             }
33558         }
33559         if (!is_total_timeover && no_player_alive_to_join())
33560         {
33561             is_total_timeover = 1;
33562             nojoin = 1;
33563         }
33564     }
33565 }
33566 
33567 
33568 // ----------------------- Update functions ------------------------------
33569 
update_scroller()33570 void update_scroller()
33571 {
33572     float to = 0;
33573     int i, againstend = 0;
33574     int numplay = 0; //4player
33575     float tx = advancex, ty = advancey;
33576     float rm = -9999, lm = 999999, bm = -9999, tm = 999999; //player boundary box
33577     static int scrolladd = 0;
33578     scrolldx = scrolldy = 0;
33579     int p_alive = 0;
33580 
33581     if(_time < level->advancetime || freezeall)
33582     {
33583         return;    // Added freezeall so backgrounds/scrolling don't update if animations are frozen
33584     }
33585 
33586     /*
33587     	//level->advancetime = _time + (GAME_SPEED/100);    // Changed so scrolling speeds up for faster players
33588     	level->advancetime = _time  -
33589     		((player[0].ent && (player[0].ent->modeldata.speed >= 12 || player[0].ent->modeldata.runspeed >= 12)) ||
33590     		 (player[1].ent && (player[1].ent->modeldata.speed >= 12 || player[1].ent->modeldata.runspeed >= 12)) ||
33591     		 (player[2].ent && (player[2].ent->modeldata.speed >= 12 || player[2].ent->modeldata.runspeed >= 12)) ||
33592     		 (player[3].ent && (player[3].ent->modeldata.speed >= 12 || player[3].ent->modeldata.runspeed >= 12)) );    // Changed so if your player is faster the backgrounds scroll faster*/
33593 
33594     level->advancetime = _time;
33595 
33596     if(level_completed)
33597     {
33598         return;
33599     }
33600 
33601     for(i = 0; i < MAX_PLAYERS; i++)
33602     {
33603         if (player[i].ent && !player[i].ent->dead)
33604         {
33605             p_alive = 1;
33606             break;
33607         }
33608     }
33609 
33610     //White Dragon: No more enemies!
33611     if(current_spawn >= level->numspawns && !findent(TYPE_ENEMY) && p_alive)
33612     /*if(current_spawn >= level->numspawns && !findent(TYPE_ENEMY) &&
33613             ((player[0].ent && !player[0].ent->dead) || (player[1].ent && !player[1].ent->dead) || (player[2].ent && !player[2].ent->dead) || (player[3].ent && !player[3].ent->dead))
33614       )*/
33615     {
33616         if(!findent(TYPE_ENDLEVEL) && ((!findent(TYPE_ITEM | TYPE_OBSTACLE) && level->type == 1) || level->type == 0)) // Feb 25, 2005 - Added so obstacles
33617         {
33618             level_completed = 1; // can be used for bonus levels
33619         }
33620     }
33621     else if(count_ents(TYPE_ENEMY) < groupmin)
33622     {
33623         while(count_ents(TYPE_ENEMY) < groupmax &&
33624                 current_spawn < level->numspawns &&
33625                 level->pos >= level->spawnpoints[current_spawn].at
33626              )
33627         {
33628             if(level->spawnpoints[current_spawn].musicfade)
33629             {
33630                 musicfade[0] = (float)level->spawnpoints[current_spawn].musicfade;
33631                 musicfade[1] = (float)savedata.musicvol;
33632             }
33633             else if(level->spawnpoints[current_spawn].music[0])
33634             {
33635                 strncpy(musicname, level->spawnpoints[current_spawn].music, MAX_BUFFER_LEN);
33636                 musicoffset = level->spawnpoints[current_spawn].musicoffset;
33637                 musicloop = 1;
33638             }
33639             else if(level->spawnpoints[current_spawn].wait)
33640             {
33641                 level->waiting = 1;
33642                 go_time = 0;
33643             }
33644             else if(level->spawnpoints[current_spawn].groupmin || level->spawnpoints[current_spawn].groupmax)
33645             {
33646                 groupmin = level->spawnpoints[current_spawn].groupmin;
33647                 groupmax = level->spawnpoints[current_spawn].groupmax;
33648             }
33649             else if(level->spawnpoints[current_spawn].nojoin != 0)
33650             {
33651                 nojoin = (level->spawnpoints[current_spawn].nojoin == 1);
33652             }
33653             else if(level->spawnpoints[current_spawn].scrollminz & 0x80000000)
33654             {
33655                 scrollminz = (float)(level->spawnpoints[current_spawn].scrollminz & 0x7fffffff);
33656                 scrollmaxz = (float)level->spawnpoints[current_spawn].scrollmaxz;
33657                 if(!_time)
33658                 {
33659                     advancey = scrollminz;    // reset y if spawn at very beginning
33660                 }
33661             }
33662             else if(level->spawnpoints[current_spawn].scrollminx & 0x80000000)
33663             {
33664                 scrollminx = (float)(level->spawnpoints[current_spawn].scrollminx & 0x7fffffff);
33665                 scrollmaxx = (float)level->spawnpoints[current_spawn].scrollmaxx;
33666             }
33667             else if(level->spawnpoints[current_spawn].blockade)
33668             {
33669                 // assume level spawn entry will not roll back, so just change it to 0 here
33670                 if(level->spawnpoints[current_spawn].blockade < 0)
33671                 {
33672                     level->spawnpoints[current_spawn].blockade = 0;
33673                 }
33674                 blockade = (float)level->spawnpoints[current_spawn].blockade;
33675             }
33676             else if(level->spawnpoints[current_spawn].palette != 0)
33677             {
33678                 // assume level spawn entry will not roll back, so just change it to 0 here
33679                 if(level->spawnpoints[current_spawn].palette < 0)
33680                 {
33681                     level->spawnpoints[current_spawn].palette = 0;
33682                 }
33683                 change_system_palette(level->spawnpoints[current_spawn].palette);
33684             }
33685             else if(level->spawnpoints[current_spawn].light.y)  // change light direction for gfxshadow
33686             {
33687                 light.x = level->spawnpoints[current_spawn].light.x;
33688                 light.y = level->spawnpoints[current_spawn].light.y;
33689             }
33690             else if(level->spawnpoints[current_spawn].shadowcolor)  // change color for gfxshadow
33691             {
33692                 shadowcolor = level->spawnpoints[current_spawn].shadowcolor;
33693                 if(shadowcolor == -1)
33694                 {
33695                     shadowcolor = 0;
33696                 }
33697                 else if(shadowcolor == -2)
33698                 {
33699                     shadowcolor = -1;
33700                 }
33701             }
33702             else if(level->spawnpoints[current_spawn].shadowalpha)  // change color for gfxshadow
33703             {
33704                 shadowalpha = level->spawnpoints[current_spawn].shadowalpha;
33705                 if(shadowalpha == -1)
33706                 {
33707                     shadowalpha = 0;
33708                 }
33709             }
33710             else if(level->spawnpoints[current_spawn].shadowopacity)  // change color for gfxshadow
33711             {
33712                 shadowopacity = level->spawnpoints[current_spawn].shadowopacity;
33713                 if(shadowopacity == -1)
33714                 {
33715                     shadowopacity = 0;
33716                 }
33717                 if(shadowopacity == -2)
33718                 {
33719                     shadowopacity = -1;
33720                 }
33721             }
33722             else
33723             {
33724                 smartspawn(&level->spawnpoints[current_spawn]);
33725             }
33726             ++current_spawn;
33727         }
33728     }
33729 
33730     for(i = 0; i < levelsets[current_set].maxplayers; i++)
33731     {
33732         if(player[i].ent)
33733         {
33734             if(player[i].ent->position.x > rm)
33735             {
33736                 rm = player[i].ent->position.x;
33737             }
33738             if(player[i].ent->position.x < lm)
33739             {
33740                 lm = player[i].ent->position.x;
33741             }
33742             if(player[i].ent->position.z > bm)
33743             {
33744                 bm = player[i].ent->position.z;
33745             }
33746             if(player[i].ent->position.z < tm)
33747             {
33748                 tm = player[i].ent->position.z;
33749             }
33750             numplay++;
33751         }
33752     }
33753 
33754     if(level->waiting)
33755     {
33756         // Wait for all enemies to be defeated
33757         if(!findent(TYPE_ENEMY))
33758         {
33759             level->waiting = 0;
33760             if(level->noreset <= 1)
33761             {
33762                 timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
33763             }
33764             go_time = _time + 3 * GAME_SPEED;
33765         }
33766     }
33767     if(numplay == 0)
33768     {
33769         return;
33770     }
33771 
33772 
33773 
33774     if(!level->waiting)
33775     {
33776         if(level->scrolldir & SCROLL_RIGHT)
33777         {
33778 
33779             againstend = (level->width <= videomodes.hRes);
33780 
33781             if(rm - lm > videomodes.hRes)
33782             {
33783                 to = advancex;
33784             }
33785             else
33786             {
33787                 to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset;
33788             }
33789 
33790             if(to < scrollminx)
33791             {
33792                 to = scrollminx;
33793             }
33794             else if(to > scrollmaxx)
33795             {
33796                 to = scrollmaxx;
33797             }
33798 
33799             if((level->scrolldir & SCROLL_BACK) && to < blockade)
33800             {
33801                 to = blockade;
33802             }
33803 
33804             if(to > advancex)
33805             {
33806                 if(to > advancex + level->scrollspeed)
33807                 {
33808                     to = advancex + level->scrollspeed;
33809                 }
33810                 advancex = to;
33811             }
33812             else if((level->scrolldir & SCROLL_BACK) && to < advancex)
33813             {
33814                 if(to < advancex - level->scrollspeed)
33815                 {
33816                     to = advancex - level->scrollspeed;
33817                 }
33818                 advancex = to;
33819             }
33820 
33821             if(advancex < 0)
33822             {
33823                 advancex = 0;
33824             }
33825             if(advancex >= level->width - videomodes.hRes)
33826             {
33827                 advancex = (float)level->width - videomodes.hRes;
33828                 againstend = 1;
33829             }
33830 
33831             if(againstend)
33832             {
33833                 level->pos++;
33834             }
33835             else
33836             {
33837                 level->pos = (int)advancex;
33838             }
33839 
33840 
33841         }
33842         else if(level->scrolldir & SCROLL_LEFT)
33843         {
33844 
33845             againstend = (level->width <= videomodes.hRes);
33846 
33847             if(rm - lm > videomodes.hRes)
33848             {
33849                 to = advancex;
33850             }
33851             else
33852             {
33853                 to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset;
33854             }
33855 
33856             if(to < scrollminx)
33857             {
33858                 to = scrollminx;
33859             }
33860             else if(to > scrollmaxx)
33861             {
33862                 to = scrollmaxx;
33863             }
33864 
33865             if((level->scrolldir & SCROLL_BACK) && level->width - videomodes.hRes - to < blockade)
33866             {
33867                 to = level->width - videomodes.hRes - blockade;
33868             }
33869 
33870             if(to < advancex)
33871             {
33872                 if(to < advancex - level->scrollspeed)
33873                 {
33874                     to = advancex - level->scrollspeed;
33875                 }
33876                 advancex = to;
33877             }
33878             else if((level->scrolldir & SCROLL_BACK) && to > advancex)
33879             {
33880                 if(to > advancex + level->scrollspeed)
33881                 {
33882                     to = advancex + level->scrollspeed;
33883                 }
33884                 advancex = to;
33885             }
33886 
33887             if(advancex > level->width - videomodes.hRes)
33888             {
33889                 advancex = (float)level->width - videomodes.hRes;
33890             }
33891             if(advancex <= 0)
33892             {
33893                 advancex = 0;
33894                 againstend = 1;
33895             }
33896 
33897             if(againstend)
33898             {
33899                 level->pos++;
33900             }
33901             else
33902             {
33903                 level->pos = (int)((level->width - videomodes.hRes) - advancex);
33904             }
33905         }
33906         else if(level->scrolldir & SCROLL_OUTWARD) // z scroll only
33907         {
33908 
33909             if(bm - tm > videomodes.vRes)
33910             {
33911                 to = advancey;
33912             }
33913             else
33914             {
33915                 to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset;
33916             }
33917 
33918             if(to < scrollminz)
33919             {
33920                 to = scrollminz;
33921             }
33922             else if(to > scrollmaxz)
33923             {
33924                 to = scrollmaxz;
33925             }
33926 
33927             if((level->scrolldir & SCROLL_BACK) && to < blockade)
33928             {
33929                 to = blockade;
33930             }
33931 
33932             if(to > advancey)
33933             {
33934                 if(to > advancey + level->scrollspeed)
33935                 {
33936                     to = advancey + level->scrollspeed;
33937                 }
33938                 advancey = to;
33939             }
33940             else if((level->scrolldir & SCROLL_BACK) && to < advancey)
33941             {
33942                 if(to < advancey - level->scrollspeed)
33943                 {
33944                     to = advancey - level->scrollspeed;
33945                 }
33946                 advancey = to;
33947             }
33948 
33949             if(advancey > panel_height - videomodes.vRes)
33950             {
33951                 advancey = (float)panel_height - videomodes.vRes;
33952                 againstend = 1;
33953             }
33954             if(advancey < 0)
33955             {
33956                 advancey = 0;
33957             }
33958 
33959             if(againstend)
33960             {
33961                 level->pos++;
33962             }
33963             else
33964             {
33965                 level->pos = (int)advancey;
33966             }
33967         }
33968         else if(level->scrolldir & SCROLL_INWARD)
33969         {
33970             if(bm - tm > videomodes.vRes)
33971             {
33972                 to = advancey;
33973             }
33974             else
33975             {
33976                 to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset;
33977             }
33978 
33979             if(to < scrollminz)
33980             {
33981                 to = scrollminz;
33982             }
33983             else if(to > scrollmaxz)
33984             {
33985                 to = scrollmaxz;
33986             }
33987 
33988             if((level->scrolldir & SCROLL_BACK) && panel_height - videomodes.vRes - to < blockade)
33989             {
33990                 to = panel_height - videomodes.vRes - blockade;
33991             }
33992 
33993             if(to < advancey)
33994             {
33995                 if(to < advancey - level->scrollspeed)
33996                 {
33997                     to = advancey - level->scrollspeed;
33998                 }
33999                 advancey = to;
34000             }
34001             else if((level->scrolldir & SCROLL_BACK) && to > advancey)
34002             {
34003                 if(to > advancey + level->scrollspeed)
34004                 {
34005                     to = advancey + level->scrollspeed;
34006                 }
34007                 advancey = to;
34008             }
34009 
34010             if(advancey > panel_height - videomodes.vRes)
34011             {
34012                 advancey = (float)panel_height - videomodes.vRes;
34013             }
34014             if(advancey <= 0)
34015             {
34016                 advancey = 0;
34017                 againstend = 1;
34018             }
34019 
34020             if(againstend)
34021             {
34022                 level->pos++;
34023             }
34024             else
34025             {
34026                 level->pos = (int)((panel_height - videomodes.vRes) - advancey);
34027             }
34028         }
34029         //up down, elevator stage
34030         else if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN))
34031         {
34032             //advancey += 0.5;
34033             if(scrolladd == 1)
34034             {
34035                 scrolladd = 0;
34036                 advancey++;
34037             }
34038             else
34039             {
34040                 scrolladd++;
34041             }
34042             level->pos = (int)advancey;
34043         }
34044     }//if(!level->waiting)
34045 
34046     // z auto-scroll
34047     if((level->scrolldir & SCROLL_LEFT) || (level->scrolldir & SCROLL_RIGHT)) // added scroll type both; weird things can happen, but only if the modder is lazy in using blockades, lol
34048     {
34049 
34050         if(cameratype == 1)
34051         {
34052             bm = -9999;
34053             tm = 999999; //recalculate
34054             for(i = 0; i < levelsets[current_set].maxplayers; i++)
34055             {
34056                 if(player[i].ent)
34057                 {
34058                     if(player[i].ent->position.z - player[i].ent->position.y > bm)
34059                     {
34060                         bm = player[i].ent->position.z - player[i].ent->position.y;
34061                     }
34062                     if(player[i].ent->position.z - player[i].ent->position.y < tm)
34063                     {
34064                         tm = player[i].ent->position.z - player[i].ent->position.y;
34065                     }
34066                 }
34067             }
34068         }
34069         if(bm - tm > videomodes.vRes)
34070         {
34071             to = advancey;
34072         }
34073         else
34074         {
34075             to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset;
34076         }
34077 
34078         // new scroll limit
34079         if(to > scrollmaxz)
34080         {
34081             to = scrollmaxz;
34082         }
34083         else if(to < scrollminz)
34084         {
34085             to = scrollminz;
34086         }
34087 
34088         if(to != advancey)
34089         {
34090             if(to > advancey + level->scrollspeed)
34091             {
34092                 to = advancey + level->scrollspeed;
34093             }
34094             else if(to < advancey - level->scrollspeed)
34095             {
34096                 to = advancey - level->scrollspeed;
34097             }
34098             advancey = (float)to;
34099         }
34100 
34101         if(advancey > panel_height - (level->rocking ? 16 : 12) - videomodes.vRes)
34102         {
34103             advancey = (float)(panel_height - (level->rocking ? 16 : 12) - videomodes.vRes);
34104         }
34105         if(advancey < 0)
34106         {
34107             advancey = 0;
34108         }
34109     }
34110     // now x auto scroll
34111     else if((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD))
34112     {
34113         if(rm - lm > videomodes.hRes)
34114         {
34115             to = advancex;
34116         }
34117         else
34118         {
34119             to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset;
34120         }
34121 
34122         // new scroll limit
34123         if(to > scrollmaxx)
34124         {
34125             to = scrollmaxx;
34126         }
34127         else if(to < scrollminx)
34128         {
34129             to = scrollminx;
34130         }
34131 
34132         if(to != advancex)
34133         {
34134             if(to > advancex + level->scrollspeed)
34135             {
34136                 to = advancex + level->scrollspeed;
34137             }
34138             else if(to < advancex - level->scrollspeed)
34139             {
34140                 to = advancex - level->scrollspeed;
34141             }
34142             advancex = (float)to;
34143         }
34144 
34145         if(advancex > level->width - videomodes.hRes)
34146         {
34147             advancex = (float)(level->width - videomodes.hRes);
34148         }
34149         if(advancex < 0)
34150         {
34151             advancex = 0;
34152         }
34153     }
34154     //end of z auto-scroll
34155     // global value for type_panel
34156     scrolldx = advancex - tx;
34157     scrolldy = advancey - ty;
34158 }
34159 
34160 
update_scrolled_bg()34161 void update_scrolled_bg()
34162 {
34163     float rocktravel;
34164     unsigned char neonp[32];//3*8
34165     static int neon_count = 0;
34166     static int rockpos = 0;
34167     static int rockoffssine[32] =
34168     {
34169         2, 2, 3, 4, 5, 6, 7, 7,
34170         8, 8, 9, 9, 9, 9, 8, 8,
34171         7, 7, 6, 5, 4, 3, 2, 2,
34172         1, 1, 0, 0, 0, 0, 1, 1
34173     };   // normal rock
34174     static int rockoffsshake[32] =
34175     {
34176         2, 2, 2, 2, 2, 2, 2, 2,
34177         2, 2, 0, 4, 2, 0, 4, 2,
34178         2, 2, 2, 2, 2, 2, 2, 2,
34179         2, 2, 0, 4, 2, 0, 4, 2,
34180     };   // slow, constant jarring rock, like on a train
34181     static int rockoffsrumble[32] =
34182     {
34183         2, 2, 3, 3, 2, 2, 3, 3,
34184         2, 2, 3, 3, 2, 3, 2, 3,
34185         2, 2, 3, 3, 2, 2, 3, 3,
34186         2, 2, 3, 3, 2, 3, 2, 3,
34187     };   // fast, constant rumbling, like in/on a van or trailer
34188     int pb = pixelbytes[(int)PIXEL_32];
34189 
34190     // Time to update neon and screen all flag false?
34191     if(_time >= neon_time && !freezeall)
34192     {
34193         memcpy(neonp, neontable + 128 * pb, 8 * pb);
34194         memcpy(neontable + 128 * pb, neonp + 2 * pb, 6 * pb);
34195         memcpy(neontable + (128 + 6)*pb, neonp, 2 * pb);
34196 
34197         neon_time = _time + (GAME_SPEED / 3);
34198         neon_count += 2;
34199     }
34200 
34201     if(!freezeall)
34202     {
34203         rocktravel = (level->rocking) ? ((_time - traveltime) / ((float)GAME_SPEED / 30)) : 0; // no like in real life, maybe
34204         if(level->bgspeed < 0)
34205         {
34206             rocktravel = -rocktravel;
34207         }
34208         bgtravelled  += (_time - traveltime) * level->bgspeed  / 30 * 4 + rocktravel;
34209         vbgtravelled += (_time - traveltime) * level->vbgspeed / 30 * 4;
34210     }
34211     else
34212     {
34213         texttime += _time - traveltime;
34214     }
34215 
34216     timevar = _time - texttime;
34217 
34218     if(level->rocking)
34219     {
34220         rockpos = (timevar / (GAME_SPEED / 8)) & 31;
34221         if(level->rocking == 1)
34222         {
34223             gfx_y_offset = level->quake - 4 - rockoffssine[rockpos];
34224         }
34225         else if(level->rocking == 2)
34226         {
34227             gfx_y_offset = level->quake - 4 - rockoffsshake[rockpos];
34228         }
34229         else if(level->rocking == 3)
34230         {
34231             gfx_y_offset = level->quake - 4 - rockoffsrumble[rockpos];
34232         }
34233     }
34234     else
34235     {
34236         if(level->quake >= 0)
34237         {
34238             gfx_y_offset = level->quake - 4;
34239         }
34240         else
34241         {
34242             gfx_y_offset = level->quake + 4;
34243         }
34244     }
34245 
34246     //if(level->scrolldir!=SCROLL_UP && level->scrolldir!=SCROLL_DOWN) gfx_y_offset -= advancey;
34247     gfx_y_offset += gfx_y_offset_adj;   //2011_04_03, DC: Apply modder adjustment.
34248 
34249     traveltime = _time;
34250 
34251     if(_time >= level->quaketime)
34252     {
34253         level->quake /= 2;
34254         level->quaketime = _time + (GAME_SPEED / 25);
34255     }
34256 }
34257 
draw_scrolled_bg()34258 void draw_scrolled_bg()
34259 {
34260     int index = 0, x, z, i = 0, j, l, m;
34261     s_layer *layer;
34262     int width, height;
34263 
34264     int vpx, vpy, vpw, vph;
34265 
34266     if(viewportw > 0)
34267     {
34268         vpx = viewportx;
34269         vpy = viewporty;
34270         vpw = viewportw;
34271         vph = viewporth;
34272     }
34273     else
34274     {
34275         vpx = vpy = 0;
34276         vpw = videomodes.hRes;
34277         vph = videomodes.vRes;
34278     }
34279 
34280     //if(level) printf("%d %d %d %d\n", vpx, vpy, vpw, vph);
34281 
34282     s_drawmethod screenmethod = plainmethod;
34283 
34284     /*s_drawmethod *pscreenmethod = &screenmethod;
34285     for(i = 0; i < level->numholes; i++)
34286     {
34287         spriteq_add_sprite((int)(level->holes[i].x - screenx + gfx_x_offset), (int)(level->holes[i].z - level->holes[i].depth - screeny + gfx_y_offset), HOLE_Z, holesprite, pscreenmethod, 0);
34288     }*/
34289 
34290     for(index = 0; index < level->numlayersref; index++)
34291     {
34292         layer = level->layersref + index;
34293 
34294         screenmethod = layer->drawmethod;
34295 
34296         //printf("layer %d, handle:%u, z:%d\n", index, layer->gfx.handle, layer->position.z);
34297 
34298 		// Layer must be enabled and have at least one instace, or we don't draw it.
34299         if(!screenmethod.xrepeat || !screenmethod.yrepeat || !layer->enabled)
34300         {
34301             continue;
34302         }
34303 
34304         width = screenmethod.xspan = layer->size.x + layer->spacing.x;
34305         height = screenmethod.yspan = layer->size.y + layer->spacing.z;
34306 
34307         x = (int)(layer->offset.x - (advancex +  bgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.x) ) ;
34308 
34309         //printf("layerxratio %f  %d %f\n ", layer->xratio, x, layer->bgspeedratio);
34310 
34311         if((level->scrolldir & SCROLL_UP))
34312         {
34313             //z = (int)(layer->offset.z + advancey * (1.0 - layer->ratio.z) ) ;
34314             z = (int)(layer->offset.z - (advancey + vbgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.z) ) ;
34315         }
34316         else
34317         {
34318             //z = (int)(layer->offset.z - advancey * (1.0 - layer->ratio.z) ) ;
34319             z = (int)(layer->offset.z - (advancey + vbgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.z) ) ;
34320         }
34321 
34322         if(layer->quake)
34323         {
34324             x += gfx_x_offset;
34325             z += gfx_y_offset;
34326             //printf("%d y %d %d\n", index, gfx_y_offset, z);
34327         }
34328 
34329         x -= vpx;
34330         z -= vpy;
34331 
34332 
34333         if(x < 0)
34334         {
34335             i = (-x) / width;
34336             x %= width;
34337         }
34338         else
34339         {
34340             i = 0;
34341         }
34342 
34343         if(i > 0 && screenmethod.water.watermode != WATER_MODE_SHEAR && screenmethod.water.amplitude)
34344         {
34345             i--;
34346             x -= width;
34347         }
34348 
34349         if(z < 0)
34350         {
34351             j = (-z) / height;
34352             z %= height;
34353         }
34354         else
34355         {
34356             j = 0;
34357         }
34358 
34359 		if(layer->neon)
34360         {
34361             screenmethod.table = neontable;
34362         }
34363         else
34364         {
34365             if(current_palette > 0)
34366             {
34367                 screenmethod.table = level->palettes[current_palette - 1];
34368             }
34369             else
34370             {
34371                 screenmethod.table = NULL;
34372             }
34373         }
34374 
34375         screenmethod.water.wavetime =  (int)(timevar * screenmethod.water.wavespeed);
34376         screenmethod.xrepeat = screenmethod.yrepeat = 0;
34377         for(m = z; j < layer->drawmethod.yrepeat && m < vph; m += height, j++, screenmethod.yrepeat++);
34378         for(l = x; i < layer->drawmethod.xrepeat && l < vpw + (screenmethod.water.watermode == WATER_MODE_SHEAR ? 0 : screenmethod.water.amplitude * 2); l += width, i++, screenmethod.xrepeat++);
34379 
34380         if(layer->gfx.screen->magic == screen_magic)
34381         {
34382             spriteq_add_screen(x + vpx, z + vpy, layer->z, layer->gfx.screen, &screenmethod, index);
34383         }
34384         else if(layer->gfx.sprite->magic == sprite_magic)
34385         {
34386             spriteq_add_frame(x + vpx, z + vpy, layer->z, layer->gfx.sprite, &screenmethod, index);
34387         }
34388 
34389         //printf("******%d\t%d\t%d\t%d\t%d*****\n", x+vpx, z+vpy, layer->.z, screenmethod.xrepeat, screenmethod.yrepeat);
34390     }
34391 
34392 
34393 }
34394 
getinterval()34395 u32 getinterval()
34396 {
34397     interval = timer_getinterval(GAME_SPEED); // so interval can be logged into movie
34398     if(interval > GAME_SPEED)
34399     {
34400         interval = 1;
34401     }
34402     if(interval > GAME_SPEED / 4)
34403     {
34404         interval = GAME_SPEED / 4;
34405     }
34406     return interval;
34407 }
34408 
34409 // Caskey, Damon V.
34410 // 2019-04-22
34411 //
34412 // Run input scripts. Similar to keys, but
34413 // execute before processing any command functions.
execute_input_scripts(int player_index)34414 void execute_input_scripts(int player_index)
34415 {
34416 	s_player *player_obj = NULL;
34417 
34418 	player_obj = player + player_index;
34419 
34420 	if (!player_obj)
34421 	{
34422 		return;
34423 	}
34424 
34425 	if (player_obj->newkeys || (keyscriptrate && player->keys) || player->releasekeys)
34426 	{
34427 		// 2019-04-22 Don't exist yet
34428 		//if (level)
34429 		//{
34430 
34431 			//execute_level_key_script(player_index, player);
34432 			//execute_entity_key_script(player.ent);
34433 		//}
34434 		//execute_key_script(player_index, player);
34435 
34436 		execute_input_script_all(player_index);
34437 	}
34438 }
34439 
inputrefresh(int playrecmode)34440 void inputrefresh(int playrecmode)
34441 {
34442     int p;
34443     s_player *pl;
34444     u64 k;
34445 
34446     control_update(playercontrolpointers, MAX_PLAYERS);
34447 
34448     bothkeys = 0;
34449     bothnewkeys = 0;
34450 
34451     for(p = 0; p < MAX_PLAYERS; p++)
34452     {
34453         pl = player + p;
34454 
34455         if ( playrecmode != A_REC_PLAY )
34456         {
34457 
34458             pl->releasekeys = (playercontrolpointers[p]->keyflags | pl->keys) - playercontrolpointers[p]->keyflags;
34459             pl->releasekeys &= ~pl->disablekeys;
34460             pl->keys = playercontrolpointers[p]->keyflags & ~pl->disablekeys;
34461             pl->newkeys = playercontrolpointers[p]->newkeyflags & ~pl->disablekeys;
34462             pl->playkeys |= pl->newkeys;
34463             pl->playkeys &= pl->keys;
34464             pl->playkeys &= ~pl->disablekeys;
34465         }
34466         else
34467         {
34468             // in play mode: add pressed keys to rec keys
34469             pl->releasekeys |= (playercontrolpointers[p]->keyflags | pl->prevkeys) - playercontrolpointers[p]->keyflags;
34470             pl->releasekeys &= ~pl->disablekeys;
34471             pl->keys |= playercontrolpointers[p]->keyflags & ~pl->disablekeys;
34472             pl->newkeys |= playercontrolpointers[p]->newkeyflags & ~pl->disablekeys;
34473             pl->playkeys |= pl->newkeys;
34474             pl->playkeys &= pl->keys;
34475             pl->playkeys &= ~pl->disablekeys;
34476         }
34477 
34478 		execute_input_scripts(p);
34479 
34480         if(pl->ent && pl->ent->movetime < _time)
34481         {
34482             memset(pl->combokey, 0, sizeof(*pl->combokey)*MAX_SPECIAL_INPUTS);
34483             memset(pl->inputtime, 0, sizeof(*pl->inputtime)*MAX_SPECIAL_INPUTS);
34484             pl->combostep = 0;
34485         }
34486         if(pl->newkeys)
34487         {
34488             k = pl->newkeys;
34489             if(pl->ent)
34490             {
34491                 pl->ent->movetime = _time + GAME_SPEED / 4;
34492                 if(k & FLAG_MOVELEFT)
34493                 {
34494                     k |= pl->ent->direction ? FLAG_BACKWARD : FLAG_FORWARD;
34495                 }
34496                 else if(k & FLAG_MOVERIGHT)
34497                 {
34498                     k |= pl->ent->direction ? FLAG_FORWARD : FLAG_BACKWARD;
34499                 }
34500             }
34501             pl->inputtime[pl->combostep] = _time;
34502             pl->combokey[pl->combostep] = k;
34503             pl->combostep++;
34504             pl->combostep %= MAX_SPECIAL_INPUTS;
34505         }
34506 
34507         bothkeys |= player[p].keys;
34508         bothnewkeys |= player[p].newkeys;
34509     }
34510 
34511 }
34512 
execute_keyscripts()34513 void execute_keyscripts()
34514 {
34515     int p;
34516     for(p = 0; p < levelsets[current_set].maxplayers; p++)
34517     {
34518         if(!_pause && (level || inScreen) && (player[p].newkeys || (keyscriptrate && player[p].keys) || player[p].releasekeys))
34519         {
34520             if(level)
34521             {
34522                 execute_level_key_script(p);
34523                 execute_entity_key_script(player[p].ent);
34524             }
34525             execute_key_script(p);
34526             execute_key_script_all(p);
34527         }
34528     }
34529 }
34530 
execute_updatescripts()34531 void execute_updatescripts()
34532 {
34533     if(Script_IsInitialized(&update_script))
34534     {
34535         Script_Execute(&(update_script));
34536     }
34537     if(level && Script_IsInitialized(&(level->update_script)))
34538     {
34539         Script_Execute(&(level->update_script));
34540     }
34541 }
34542 
execute_updatedscripts()34543 void execute_updatedscripts()
34544 {
34545     if(Script_IsInitialized(&updated_script))
34546     {
34547         Script_Execute(&(updated_script));
34548     }
34549     if(level && Script_IsInitialized(&(level->updated_script)))
34550     {
34551         Script_Execute(&(level->updated_script));
34552     }
34553 }
34554 
draw_textobjs()34555 void draw_textobjs()
34556 {
34557     int i;
34558     s_textobj *textobj;
34559     if(!level)
34560     {
34561         return;
34562     }
34563     for(i = 0; i < level->numtextobjs ; i++)
34564     {
34565         textobj = level->textobjs + i;
34566 
34567         if(textobj->time && textobj->time <= _time)		//If a time was set and passed, remove the text object.
34568         {
34569             level->textobjs[i].time	= 0;
34570             level->textobjs[i].position.x = 0;
34571             level->textobjs[i].position.y = 0;
34572             level->textobjs[i].font = 0;
34573             level->textobjs[i].position.z = 0;
34574             if(level->textobjs[i].text)
34575             {
34576                 free(level->textobjs[i].text);
34577                 level->textobjs[i].text = NULL;
34578             }
34579         }
34580         else
34581         {
34582             if(textobj->text)
34583             {
34584                 font_printf(textobj->position.x, textobj->position.y, textobj->font, textobj->position.z, "%s", textobj->text);
34585             }
34586         }
34587     }
34588 }
34589 
recordInputs()34590 int recordInputs()
34591 {
34592     int p = 0;
34593     RecKeys reckey;
34594     unsigned int window = 4096;
34595     u32 max_rec_time = GAME_SPEED*60*10; // protection
34596 
34597     if(playrecstatus->status != A_REC_REC) return 0;
34598     if ( !playrecstatus->begin )
34599     {
34600         playrecstatus->starttime = _time;
34601         playrecstatus->synctime = 0;
34602         if (playrecstatus->buffer)
34603         {
34604             free(playrecstatus->buffer);
34605             playrecstatus->buffer = NULL;
34606         }
34607         playrecstatus->buffer = (RecKeys*)calloc(window+playrecstatus->synctime*sizeof(RecKeys),sizeof(RecKeys));
34608         if (playrecstatus->buffer == NULL)
34609         {
34610             printf("Error to allocate buffer in record inputs mode.\n");
34611             return 0;
34612         }
34613         playrecstatus->begin = 1;
34614         playrecstatus->seed = getseed();
34615         playrecstatus->cseed = _time;
34616         srand(_time);
34617         playrecstatus->ticks = timer_gettick();
34618     }
34619     else
34620     {
34621         if ( playrecstatus->synctime%window >= window-2 ) // last is NULL bytes
34622         {
34623             playrecstatus->buffer = (RecKeys*)realloc(playrecstatus->buffer,sizeof(RecKeys)*((int)(trunc(playrecstatus->synctime/window)+1)*window+window));
34624             if (playrecstatus->buffer == NULL)
34625             {
34626                 printf("Error to allocate buffer in record inputs mode.\n");
34627                 return 0;
34628             } else memset(playrecstatus->buffer+(playrecstatus->synctime+1),0,(int)(trunc(playrecstatus->synctime/window)+1)*window+window-(playrecstatus->synctime+1)-1); // -2 becouse -1 is to 0 to size-1
34629         }
34630     }
34631 
34632     // now rec!
34633     if(playrecstatus->buffer && playrecstatus->begin)
34634     {
34635         for ( p = 0; p < MAX_PLAYERS; p++ )
34636         {
34637             reckey.keys[p]        = player[p].keys;
34638             reckey.newkeys[p]     = player[p].newkeys;
34639             reckey.releasekeys[p] = player[p].releasekeys;
34640             reckey.playkeys[p]    = player[p].playkeys;
34641         }
34642         reckey.time     = _time;
34643         reckey.interval = interval;
34644         reckey.synctime = playrecstatus->synctime;
34645         //reckey.seed     = getseed();
34646         memcpy( &playrecstatus->buffer[playrecstatus->synctime], &reckey, sizeof(reckey) );
34647     }
34648 
34649     if ( _time >= max_rec_time ) stopRecordInputs(); // safe
34650     if(playrecstatus->status == A_REC_REC) ++playrecstatus->synctime;
34651 
34652     //debug_printf("time: %d sync: %d",(u32)time,(u32)playrecstatus->synctime);
34653     //debug_printf("keys: %d",player[0].releasekeys&FLAG_ATTACK);
34654 
34655     return 1;
34656 }
34657 
playRecordedInputs()34658 int playRecordedInputs()
34659 {
34660     int p = 0;
34661     RecKeys reckey;
34662     char path[MAX_ARG_LEN + 1];
34663     size_t filesize = 0;
34664     char header[6];
34665 
34666     if(playrecstatus->status != A_REC_PLAY) return 0;
34667     if ( !playrecstatus->begin ) //time >= playrecstatus->starttime &&
34668     {
34669         if ( strlen(playrecstatus->path) <= 0 ) getBasePath(path, "Saves", 0);
34670         else strcpy(path,playrecstatus->path);
34671         if ( path[strlen(path)-1] != '/' ) strcpy(path,"/");
34672 
34673         if (playrecstatus->buffer)
34674         {
34675             free(playrecstatus->buffer);
34676             playrecstatus->buffer = NULL;
34677         }
34678         if(playrecstatus->handle)
34679         {
34680             fclose(playrecstatus->handle);
34681         }
34682 
34683         playrecstatus->handle = fopen(strcat(path,playrecstatus->filename), "rb+");
34684         if(playrecstatus->handle)
34685         {
34686             fseek(playrecstatus->handle, 0L, SEEK_END);
34687             filesize = ftell(playrecstatus->handle);
34688             fseek(playrecstatus->handle, 0L, SEEK_SET); // or rewind(playrecstatus->handle)
34689 
34690             if (filesize <= 0)
34691             {
34692                 printf("Empty recorded inputs file.\n");
34693                 return 0;
34694             }
34695             playrecstatus->buffer = (RecKeys*)calloc(1,filesize+1);
34696             if(!playrecstatus->buffer)
34697             {
34698                 printf("Error to allocate buffer for recorded inputs.\n");
34699                 return 0;
34700             }
34701         } else
34702         {
34703             printf("Error to open recorded inputs file.\n");
34704             return 0;
34705         }
34706         fread(&header, 6, 1, playrecstatus->handle);
34707         fread(&playrecstatus->starttime, sizeof(u32), 1, playrecstatus->handle);
34708         fread(&playrecstatus->endtime, sizeof(u32), 1, playrecstatus->handle);
34709         fread(&playrecstatus->totsynctime, sizeof(u32), 1, playrecstatus->handle);
34710         fread(&playrecstatus->cseed, sizeof(u32), 1, playrecstatus->handle);
34711         fread(&playrecstatus->seed, sizeof(unsigned long), 1, playrecstatus->handle);
34712         fread(&playrecstatus->ticks, sizeof(unsigned), 1, playrecstatus->handle);
34713         fread(playrecstatus->buffer, sizeof(RecKeys)*(playrecstatus->endtime+1), 1, playrecstatus->handle);
34714 
34715         // sync at start time
34716         _time = playrecstatus->starttime;
34717         playrecstatus->synctime = 0;
34718         playrecstatus->begin = 1;
34719         srand(playrecstatus->cseed);
34720         srand32(playrecstatus->seed);
34721         //set_ticks(timer_gettick()-playrecstatus->ticks);
34722     }
34723 
34724     // now play!
34725     if(playrecstatus->buffer && playrecstatus->begin && playrecstatus->synctime < playrecstatus->totsynctime)
34726     {
34727         memcpy( &reckey, &playrecstatus->buffer[playrecstatus->synctime], sizeof(reckey) );
34728 
34729         if( _time != reckey.time )
34730         {
34731             u32 nextsynctime = reckey.synctime;
34732 
34733             //time = (u32)reckey.time;
34734             printf("Play recorded inputs: Out of sync! Time: %d, RecTime: %d\n",time,reckey.time);
34735             /*if ( interval != reckey.interval )
34736             {
34737                 //interval = (u32)reckey.interval;
34738                 printf("Play recorded inputs: Out of sync! Interval: %d, RecInterval: %d\n",interval,reckey.interval);
34739             }*/
34740 
34741             while( _time > reckey.time && nextsynctime > 0 ) {
34742                 memcpy( &reckey, &playrecstatus->buffer[--nextsynctime], sizeof(reckey) );
34743             }
34744             _time = reckey.time;
34745 
34746             while( _time < reckey.time && nextsynctime < playrecstatus->totsynctime ) {
34747                 memcpy( &reckey, &playrecstatus->buffer[++nextsynctime], sizeof(reckey) );
34748             }
34749             _time = reckey.time;
34750         }
34751 
34752         if ( _time == reckey.time )
34753         {
34754             if ( playrecstatus->synctime != reckey.synctime )
34755             {
34756                 u32 nextsynctime = reckey.synctime;
34757 
34758                 printf("Play recorded inputs: Out of sync! SyncTime: %d, RecSyncTime: %d\n",playrecstatus->synctime,reckey.synctime);
34759 
34760                 while( playrecstatus->synctime > reckey.synctime && nextsynctime > 0 ) {
34761                     memcpy( &reckey, &playrecstatus->buffer[--nextsynctime], sizeof(reckey) );
34762                 }
34763                 playrecstatus->synctime = reckey.synctime;
34764 
34765                 while( playrecstatus->synctime < reckey.synctime && nextsynctime < playrecstatus->totsynctime ) {
34766                     memcpy( &reckey, &playrecstatus->buffer[++nextsynctime], sizeof(reckey) );
34767                 }
34768                 playrecstatus->synctime = reckey.synctime;
34769             }
34770 
34771             if ( interval != reckey.interval ) interval = reckey.interval;
34772             //srand32(reckey.seed);
34773             for ( p = 0; p < MAX_PLAYERS; p++ )
34774             {
34775                 player[p].keys        = reckey.keys[p];
34776                 player[p].newkeys     = reckey.newkeys[p];
34777                 player[p].releasekeys = reckey.releasekeys[p];
34778                 player[p].playkeys    = reckey.playkeys[p];
34779             }
34780             //inputrefresh(playrecstatus->status);
34781         }
34782     }
34783 
34784     //debug_printf("synctim: %d totsync: %d status:%d",playrecstatus->synctime,playrecstatus->totsynctime,playrecstatus->status);
34785     if(playrecstatus->status == A_REC_PLAY) ++playrecstatus->synctime;
34786     if ( playrecstatus->synctime >= playrecstatus->totsynctime || _time >= playrecstatus->endtime ) stopRecordInputs();
34787 
34788     //debug_printf("time: %d sync: %d",(u32)time,(u32)playrecstatus->synctime);
34789     //debug_printf("keys: %d",player[0].releasekeys&FLAG_ATTACK);
34790 
34791     return 1;
34792 }
34793 
stopRecordInputs()34794 int stopRecordInputs()
34795 {
34796     if(playrecstatus)
34797     {
34798         switch(playrecstatus->status)
34799         {
34800             case A_REC_REC:
34801             {
34802                 char path[MAX_ARG_LEN + 1];
34803                 char header[6] = "INP10";
34804 
34805                 if ( strlen(playrecstatus->path) <= 0 ) getBasePath(path, "Saves", 0);
34806                 else strcpy(path,playrecstatus->path);
34807                 if ( path[strlen(path)-1] != '/' ) strcpy(path,"/");
34808 
34809                 if (playrecstatus->buffer)
34810                 {
34811                     playrecstatus->handle = fopen(strcat(path,playrecstatus->filename), "wb+");
34812                     if(playrecstatus->handle)
34813                     {
34814                         playrecstatus->endtime = _time;
34815                         if (playrecstatus->synctime < 2) playrecstatus->synctime = 2;
34816                         else playrecstatus->synctime -= 2;
34817 
34818                         fwrite(header, 6, 1, playrecstatus->handle);
34819                         fwrite(&playrecstatus->starttime, sizeof(u32), 1, playrecstatus->handle);
34820                         fwrite(&playrecstatus->endtime, sizeof(u32), 1, playrecstatus->handle);
34821                         fwrite(&playrecstatus->synctime, sizeof(u32), 1, playrecstatus->handle);
34822                         fwrite(&playrecstatus->cseed, sizeof(u32), 1, playrecstatus->handle);
34823                         fwrite(&playrecstatus->seed, sizeof(unsigned long), 1, playrecstatus->handle);
34824                         fwrite(&playrecstatus->ticks, sizeof(unsigned), 1, playrecstatus->handle);
34825                         fwrite(playrecstatus->buffer, sizeof(RecKeys)*(playrecstatus->synctime+1), 1, playrecstatus->handle);
34826                         fflush(playrecstatus->handle); // safe
34827                         fclose(playrecstatus->handle);
34828                     } else return 0;
34829 
34830                     free(playrecstatus->buffer);
34831                     playrecstatus->buffer = NULL;
34832                 } else return 0;
34833                 break;
34834             }
34835             case A_REC_PLAY:
34836             {
34837                 if(playrecstatus->handle)
34838                 {
34839                     if (playrecstatus->handle) fclose(playrecstatus->handle);
34840                     else return 0;
34841                 }
34842                 break;
34843             }
34844             case A_REC_STOP:
34845             {
34846                 if(playrecstatus->handle)
34847                 {
34848                     if (playrecstatus->handle) fclose(playrecstatus->handle);
34849                     else return 0;
34850                 }
34851                 break;
34852             }
34853         }
34854 
34855         playrecstatus->status = A_REC_STOP;
34856         playrecstatus->begin = 0;
34857         playrecstatus->synctime = 0;
34858         freeRecordedInputs();
34859     } return 0;
34860 
34861     return 1;
34862 }
34863 
freeRecordedInputs()34864 int freeRecordedInputs()
34865 {
34866     playrecstatus->status = A_REC_STOP;
34867     if(playrecstatus->handle) fclose(playrecstatus->handle);
34868     if(playrecstatus->buffer)
34869     {
34870         free(playrecstatus->buffer);
34871         playrecstatus->buffer = NULL;
34872         return 1;
34873     }
34874 
34875     return 0;
34876 }
34877 
init_input_recorder()34878 a_playrecstatus* init_input_recorder()
34879 {
34880     playrecstatus = (a_playrecstatus*)calloc(1,sizeof(*playrecstatus));
34881 
34882     return playrecstatus;
34883 }
34884 
free_input_recorder()34885 void free_input_recorder()
34886 {
34887     if(playrecstatus)
34888     {
34889         if(playrecstatus->buffer)
34890         {
34891             if(playrecstatus->handle) fclose(playrecstatus->handle);
34892             free(playrecstatus->buffer);
34893             playrecstatus->buffer = NULL;
34894         }
34895         free(playrecstatus);
34896         playrecstatus = NULL;
34897     }
34898 }
34899 
update(int ingame,int usevwait)34900 void update(int ingame, int usevwait)
34901 {
34902     int i = 0;
34903     int p_keys = 0;
34904 
34905     getinterval();
34906     if(playrecstatus->status == A_REC_PLAY && !_pause && level) if ( !playRecordedInputs() ) stopRecordInputs();
34907     inputrefresh(playrecstatus->status);
34908     if(playrecstatus->status == A_REC_REC && !_pause && level) if ( !recordInputs() ) stopRecordInputs();
34909 
34910     if ((!_pause && ingame == 1) || alwaysupdate)
34911     {
34912         execute_updatescripts();
34913     }
34914 
34915     newtime = 0;
34916     if(!_pause)
34917     {
34918         if(ingame == 1 || inScreen)
34919         {
34920             execute_keyscripts();
34921         }
34922 
34923         if((level_completed && level->boss_slow == BOSS_SLOW_ON && !tospeedup) || slowmotion.toggle > SLOW_MOTION_OFF)
34924         {
34925             if(slowmotion.duration == slowmotion.counter)
34926             {
34927                 newtime = _time + interval;
34928             }
34929         }
34930         else
34931         {
34932             newtime = _time + interval;
34933         }
34934 
34935         slowmotion.counter++;
34936         if(slowmotion.counter == (slowmotion.duration + 1))
34937         {
34938             slowmotion.counter = 0;
34939             if(slowmotion.toggle > SLOW_MOTION_ON)
34940             {
34941                 slowmotion.duration = slowmotion.toggle;
34942             }
34943         }
34944         if(newtime > _time + 100)
34945         {
34946             newtime = _time + 100;
34947         }
34948 
34949         while(_time < newtime)
34950         {
34951             if(ingame == 1)
34952             {
34953                 update_scroller();
34954                 if(!freezeall)
34955                 {
34956                     int all_p_alive = 0;
34957 
34958                     for(i = 0; i < MAX_PLAYERS; i++)
34959                     {
34960                         if (!player[i].ent) ++all_p_alive;
34961                     }
34962                     all_p_alive = (all_p_alive >= MAX_PLAYERS) ? 1 : 0;
34963 
34964                     if(level->settime > 0 || (level->type != 2 && all_p_alive))
34965                     //if(level->settime > 0 || (level->type != 2 && !player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent))
34966                     {
34967                         int all_p_nojoin = 0, all_p_nocredits = 0;
34968 
34969                         for(i = 0; i < MAX_PLAYERS; i++)
34970                         {
34971                             if (!player[i].joining) ++all_p_nojoin;
34972                         }
34973                         all_p_nojoin = (all_p_nojoin >= MAX_PLAYERS) ? 1 : 0;
34974 
34975                         for(i = 0; i < MAX_PLAYERS; i++)
34976                         {
34977                             if (player[i].credits < 1) ++all_p_nocredits;
34978                         }
34979                         all_p_nocredits = (all_p_nocredits >= MAX_PLAYERS) ? 1 : 0;
34980 
34981                         if(timeleft > 0)
34982                         {
34983                             --timeleft;
34984                         }
34985                         else if((level->settime > 0 && all_p_nojoin) ||
34986                                 ( ((!noshare && credits < 1) || (noshare && all_p_nocredits))
34987                                  && all_p_nojoin )
34988                                )
34989                         {
34990                             time_over();
34991                         }
34992                     }
34993                 }
34994                 update_scrolled_bg();
34995                 if(level->type != 2)
34996                 {
34997                     updatestatus();
34998                 }
34999             }
35000             if(ingame == 1 || inScreen)
35001             {
35002                 update_ents();
35003             }
35004             ++_time;
35005         }
35006 
35007     }
35008 
35009     /************ gfx queueing ************/
35010 
35011     clearscreen(vscreen);
35012 
35013     if(ingame == 1 && !_pause)
35014     {
35015         draw_scrolled_bg();
35016         if(level->type != 2)
35017         {
35018             predrawstatus();
35019         }
35020         if(level->type != 2)
35021         {
35022             drawstatus();
35023         }
35024         draw_textobjs();
35025     }
35026 
35027     if(!ingame)
35028     {
35029         if(background)
35030         {
35031             spriteq_add_screen(0, 0, MIN_INT, background, NULL, 0);
35032         }
35033     }
35034 
35035     // entity sprites queueing
35036     if(ingame == 1 || inScreen)
35037         if(!_pause)
35038         {
35039             display_ents();
35040         }
35041 
35042     /************ updated script  ************/
35043     if(ingame == 1 || alwaysupdate)
35044     {
35045         execute_updatedscripts();
35046     }
35047 
35048     for(i = 0; i < MAX_PLAYERS; i++)
35049     {
35050         if (player[i].ent && (player[i].newkeys & FLAG_START))
35051         {
35052             p_keys = 1;
35053             break;
35054         }
35055     }
35056 
35057     // 2011/10/22 UT: move pause menu logic here
35058     /*if(ingame == 1 && !_pause && !nopause &&
35059             ((player[0].ent && (player[0].newkeys & FLAG_START)) ||
35060              (player[1].ent && (player[1].newkeys & FLAG_START)) ||
35061              (player[2].ent && (player[2].newkeys & FLAG_START)) ||
35062              (player[3].ent && (player[3].newkeys & FLAG_START)))
35063       )*/
35064     if(ingame == 1 && !_pause && !nopause && p_keys)
35065     {
35066         if ( !(goto_mainmenu_flag&1) )
35067         {
35068             sound_pause_music(1);
35069             sound_pause_sample(1);
35070             sound_play_sample(SAMPLE_PAUSE, 0, savedata.effectvol, savedata.effectvol, 100);
35071             pausemenu();
35072             return;
35073         }
35074     }
35075     if( ingame == 1 && (goto_mainmenu_flag&1) )
35076     {
35077         backto_mainmenu();
35078         return;
35079     }
35080 
35081     /********** update screen **************/
35082 
35083     spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0); // notice, always draw sprites at the very end of other methods
35084 
35085     if(_pause != 2 && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT))
35086     {
35087         screenshot(vscreen, getpal, 1);
35088     }
35089 
35090     // Debug stuff, should not appear on screenshot
35091     if(debug_time == 0xFFFFFFFF)
35092     {
35093         debug_time = _time + GAME_SPEED * 5;
35094     }
35095     if(_time < debug_time && debug_msg[0])
35096     {
35097         if( debug_xy_msg.x >= 0 && debug_xy_msg.y >= 0 )
35098         {
35099             if ( debug_xy_msg.font_index < 0 ) debug_xy_msg.font_index = 0;
35100             screen_printf(vscreen, debug_xy_msg.x, debug_xy_msg.y, debug_xy_msg.font_index, debug_msg);
35101         } else screen_printf(vscreen, 0, videomodes.vRes-fontheight(0), 0, debug_msg);
35102     }
35103     else
35104     {
35105         debug_msg[0] = 0;
35106         debug_xy_msg.x = -1;
35107         debug_xy_msg.y = -1;
35108 #ifdef DEBUG_MODE
35109         if(level->pos)
35110         {
35111             debug_printf("Position: %i, width: %i, spawn: %i, offsets: %i/%i", level->pos, level->width, current_spawn, level->quake, gfx_y_offset);
35112         }
35113 #endif
35114     }
35115 
35116     if(usevwait)
35117     {
35118         vga_vwait();
35119     }
35120     video_copy_screen(vscreen);
35121     spriteq_clear();
35122 
35123     check_music();
35124     sound_update_music();
35125 }
35126 
35127 
35128 
35129 
35130 // ----------------------------------------------------------------------
35131 /* Plombo 9/4/2010: New function that can use brightness/gamma correction
35132  * independent from the global palette on platforms where it's available.
35133  * Hardware accelerated brightness/gamma correction is available on Wii and
35134  * OpenGL platforms using TEV and GLSL, respectively. Returns 1 on success, 0 on
35135  * error. */
set_color_correction(int gm,int br)35136 int set_color_correction(int gm, int br)
35137 {
35138 #if WII || SDL || VITA
35139     video_set_color_correction(gm, br);
35140     return 1;
35141 #else
35142     return 0;
35143 #endif
35144 }
35145 
35146 // Simple palette fade / vscreen fade
fade_out(int type,int speed)35147 void fade_out(int type, int speed)
35148 {
35149     int i, j = 0;
35150     int b, g = 0;
35151     u32 interval = 0;
35152     int current = speed ? speed : fade;
35153     s_screen *fbuffer = NULL;
35154     s_drawmethod dm = plainmethod;
35155     dm.alpha = BLEND_MODE_AVERAGE;
35156 
35157     for(i = 0, j = 0; j < 64; )
35158     {
35159         while(j <= i && j < 64)
35160         {
35161             if(!type || type == 1)
35162             {
35163                 b = ((savedata.brightness + 256) * (64 - j) / 64) - 256;
35164                 g = 256 - ((savedata.gamma + 256) * (64 - j) / 64);
35165                 vga_vwait();
35166                 if(!set_color_correction(g, b))
35167                 {
35168                     if(!fbuffer)
35169                     {
35170                         fbuffer = allocscreen(vscreen->width, vscreen->height, vscreen->pixelformat);
35171                         copyscreen(fbuffer, vscreen);
35172                     }
35173                     //255 + alpha BLEND_MODE_AVERAGE is actually half blend, so use 254 instead
35174                     dm.channelr = dm.channelg = dm.channelb = 254 * (64 - j) / 64;
35175                     clearscreen(vscreen);
35176                     putscreen(vscreen, fbuffer, 0, 0, &dm);
35177                 }
35178             }
35179             j++;
35180             if(!type || type == 1)
35181             {
35182                 video_copy_screen(vscreen);
35183             }
35184         }
35185         if(!type || type == 2)
35186         {
35187             sound_update_music();
35188             if(!musicoverlap)
35189             {
35190                 sound_volume_music(savedata.musicvol * (64 - j) / 64, savedata.musicvol * (64 - j) / 64);
35191             }
35192         }
35193         interval = timer_getinterval(current);
35194         if(interval > current)
35195         {
35196             interval = current / 60;
35197         }
35198         if(interval > current / 4)
35199         {
35200             interval = current / 4;
35201         }
35202         i += interval;
35203     }
35204 
35205     if(!type || type == 2)
35206     {
35207         if(!musicoverlap)
35208         {
35209             sound_close_music();
35210         }
35211     }
35212 
35213     if(!type || type == 1)
35214     {
35215         clearscreen(vscreen);
35216         video_copy_screen(vscreen);
35217         vga_vwait();
35218         //the black screen, so we return to normal palette
35219         set_color_correction(savedata.gamma, savedata.brightness);
35220     }
35221 
35222     if(fbuffer)
35223     {
35224         freescreen(&fbuffer);
35225     }
35226 }
35227 
35228 
35229 
apply_controls()35230 void apply_controls()
35231 {
35232 #if 0 // TODO
35233     int p;
35234 
35235     for(p = 0; p < MAX_PLAYERS; p++)
35236     {
35237         control_setkey(playercontrolpointers[p], FLAG_ESC,        CONTROL_ESC);
35238         control_setkey(playercontrolpointers[p], FLAG_MOVEUP,     savedata.keys[p][SDID_MOVEUP]);
35239         control_setkey(playercontrolpointers[p], FLAG_MOVEDOWN,   savedata.keys[p][SDID_MOVEDOWN]);
35240         control_setkey(playercontrolpointers[p], FLAG_MOVELEFT,   savedata.keys[p][SDID_MOVELEFT]);
35241         control_setkey(playercontrolpointers[p], FLAG_MOVERIGHT,  savedata.keys[p][SDID_MOVERIGHT]);
35242         control_setkey(playercontrolpointers[p], FLAG_ATTACK,     savedata.keys[p][SDID_ATTACK]);
35243         control_setkey(playercontrolpointers[p], FLAG_ATTACK2,    savedata.keys[p][SDID_ATTACK2]);
35244         control_setkey(playercontrolpointers[p], FLAG_ATTACK3,    savedata.keys[p][SDID_ATTACK3]);
35245         control_setkey(playercontrolpointers[p], FLAG_ATTACK4,    savedata.keys[p][SDID_ATTACK4]);
35246         control_setkey(playercontrolpointers[p], FLAG_JUMP,       savedata.keys[p][SDID_JUMP]);
35247         control_setkey(playercontrolpointers[p], FLAG_SPECIAL,    savedata.keys[p][SDID_SPECIAL]);
35248         control_setkey(playercontrolpointers[p], FLAG_START,      savedata.keys[p][SDID_START]);
35249         control_setkey(playercontrolpointers[p], FLAG_SCREENSHOT, savedata.keys[p][SDID_SCREENSHOT]);
35250     }
35251 #endif
35252 }
35253 
35254 
35255 
35256 // ----------------------------------------------------------------------
35257 
display_credits()35258 void display_credits()
35259 {
35260     u32 finishtime = _time + 10 * GAME_SPEED;
35261     int done = 0;
35262     int s = videomodes.vShift / 2 + 3;
35263     int v = (videomodes.vRes - videomodes.vShift) / 24;
35264     int m = 0;
35265     int h = videomodes.hRes / 2;
35266     int col1 = h - fontmonowidth(0) * 16;
35267     int col2 = h + fontmonowidth(0) * 4;
35268 
35269     if(savedata.logo != 1)
35270     {
35271         return;
35272     }
35273     fade_out(0, 0);
35274 
35275     unload_level();
35276 
35277     bothnewkeys = 0;
35278 
35279     while(!done)
35280     {
35281         m = 2;
35282 
35283         font_printf(_strmidx(2, "Credits"), s,  2, 0, "Credits");
35284 
35285         font_printf(_strmidx(1, "OpenBOR"), s + v * m,  1, 0, "OpenBOR"); ++m;
35286 
35287         font_printf(col1, s + v * m,  0, 0, "Caskey, Damon V.");
35288         font_printf(col2, s + v * m,  0, 0, "Project Lead"); ++m;
35289 
35290         font_printf(col1, s + v * m,  0, 0, "Msmalik681");
35291         font_printf(col2, s + v * m,  0, 0, "Developer"); ++m;
35292 
35293         font_printf(col1, s + v * m,  0, 0, "Plombo");
35294         font_printf(col2, s + v * m,  0, 0, "Developer"); ++m;
35295 
35296         font_printf(_strmidx(1, "Former Staff"), s + v * m,  1, 0, "Former Staff"); ++m;
35297 
35298         font_printf(col1, s + v * m, 0, 0, "Fightn Words");
35299         font_printf(col2, s + v * m,  0, 0, "Fugue"); ++m;
35300         font_printf(col1, s + v * m, 0, 0, "KBAndressen");
35301         font_printf(col2, s + v * m,  0, 0, "Kirby"); ++m;
35302         font_printf(col1, s + v * m,  0, 0, "LordBall");
35303         font_printf(col2, s + v * m, 0, 0, "Orochi_X");  ++m;
35304         font_printf(col1, s + v * m, 0, 0, "SX");
35305         font_printf(col2, s + v * m,  0, 0, "Tails"); ++m;
35306         font_printf(col1, s + v * m,  0, 0, "uTunnels");
35307 		font_printf(col2, s + v * m,  0, 0, "White Dragon"); ++m;
35308 
35309         font_printf(_strmidx(1, "Ports"), s + v * m,  1, 0, "Ports"); ++m;
35310         font_printf(col1, s + v * m, 0, 0, "PSP/Linux/OSX");
35311         font_printf(col2, s + v * m, 0, 0, "SX"); ++m;
35312 		/*
35313         font_printf(col1, s + v * m, 0, 0, "OpenDingux");
35314         font_printf(col2, s + v * m, 0, 0, "Shin-NiL"); ++m;
35315 
35316         font_printf(col1, s + v * m, 0, 0, "DreamCast");
35317         font_printf(col2, s + v * m, 0, 0, "Neill Corlett, SX"); ++m;
35318 		*/
35319         font_printf(col1, s + v * m, 0, 0, "Wii");
35320         font_printf(col2, s + v * m, 0, 0, "Plombo, SX, Msmalik681"); ++m;
35321 
35322         font_printf(col1, s + v * m, 0, 0, "Android");
35323         font_printf(col2, s + v * m, 0, 0, "CRxTRDude, Plombo,"); ++m;
35324         font_printf(col2, s + v * m, 0, 0, "uTunnels, Msmalik681"); ++m;
35325         font_printf(col2, s + v * m, 0, 0, "White Dragon"); ++m;
35326 
35327         font_printf(col1,  s + v * m, 0, 0, "PS Vita");
35328         font_printf(col2, s + v * m, 0, 0, "Plombo"); ++m;
35329 
35330         update(2, 0);
35331 
35332         done |= (_time > finishtime);
35333         done |= (bothnewkeys & (FLAG_START + FLAG_ESC));
35334     }
35335     fade = 75;
35336     fade_out(0, 0);
35337 }
35338 
35339 
borShutdown(int status,char * msg,...)35340 void borShutdown(int status, char *msg, ...)
35341 {
35342     char buf[1024] = "";
35343     va_list arglist;
35344     int i;
35345 
35346     static int shuttingdown = 0;
35347 
35348     if(shuttingdown)
35349     {
35350         return;
35351     }
35352 
35353     shuttingdown = 1;
35354 
35355     //printf("savedata.logo %d\n", savedata.logo);
35356 
35357     va_start(arglist, msg);
35358     vsprintf(buf, msg, arglist);
35359     va_end(arglist);
35360 
35361     if(!disablelog)
35362     {
35363         switch(status)
35364         {
35365         case 0:
35366             printf("\n************ Shutting Down ************\n\n");
35367             break;
35368         default:
35369             printf("\n********** An Error Occurred **********"
35370                    "\n*            Shutting Down            *\n\n");
35371             break;
35372         }
35373     }
35374 
35375     if(!disablelog)
35376     {
35377         printf("%s", buf);
35378     }
35379 
35380 
35381     getRamStatus(BYTES);
35382     savesettings();
35383 
35384     enginecreditsScreen = 1;		//entry point for the engine credits screen.
35385 
35386     if(status != 2)
35387     {
35388         display_credits();
35389     }
35390 
35391     if(startup_done)
35392     {
35393         enginecreditsScreen = 0; //once the engine credits is done, disable flag.
35394         term_videomodes();
35395     }
35396 
35397     if(!disablelog)
35398     {
35399         printf("Release level data");
35400     }
35401     if (startup_done)
35402     {
35403         unload_levelorder();
35404     }
35405     if(!disablelog)
35406     {
35407         printf("...........\n");
35408     }
35409     if(startup_done)
35410     {
35411         unload_level();
35412     }
35413     if(!disablelog)
35414     {
35415         printf("Done!\n\n");
35416     }
35417 
35418     if(!disablelog)
35419     {
35420         printf("Release graphics data");
35421     }
35422     if(!disablelog)
35423     {
35424         printf("..");
35425     }
35426     if(startup_done)
35427     {
35428         freescreen(&vscreen);    // allocated by init_videomodes
35429     }
35430     if(startup_done && pixelformat == PIXEL_x8) for(i = 0; i < MAX_BLENDINGS; i++)
35431         {
35432             free(blendtables[i]);
35433         }
35434     if(!disablelog)
35435     {
35436         printf("..");
35437     }
35438     if(startup_done)
35439     {
35440         freescreen(&background);
35441     }
35442     if(!disablelog)
35443     {
35444         printf("..");
35445     }
35446 #ifdef CACHE_BACKGROUNDS
35447     if(startup_done) for(i = 0; i < MAX_CACHED_BACKGROUNDS; i++)
35448         {
35449             freescreen(&bg_cache[i]);
35450         }
35451     if(!disablelog)
35452     {
35453         printf("..");
35454     }
35455 #endif
35456     if(startup_done)
35457     {
35458         freesprites();
35459     }
35460     if(!disablelog)
35461     {
35462         printf("..");
35463     }
35464     if(startup_done)
35465     {
35466         unload_all_fonts();
35467     }
35468     if(!disablelog)
35469     {
35470         printf("\tDone!\n");
35471     }
35472 
35473 
35474     if(!disablelog)
35475     {
35476         printf("Release game data............\n\n");
35477     }
35478 
35479     if(startup_done)
35480     {
35481         free_ents();
35482     }
35483     if(startup_done)
35484     {
35485         free_models();
35486     }
35487     if(startup_done)
35488     {
35489         free_modelcache();
35490     }
35491     if(startup_done)
35492     {
35493         clear_scripts();
35494     }
35495     if(!disablelog)
35496     {
35497         printf("\nRelease game data............\tDone!\n");
35498     }
35499 
35500     if(!disablelog)
35501     {
35502         printf("Release timer................");
35503     }
35504     if(startup_done)
35505     {
35506         borTimerExit();
35507     }
35508     if(!disablelog)
35509     {
35510         printf("\tDone!\n");
35511     }
35512 
35513     if(!disablelog)
35514     {
35515         printf("Release input hardware.......");
35516     }
35517     if(startup_done)
35518     {
35519         control_exit();
35520     }
35521     if(!disablelog)
35522     {
35523         printf("\tDone!\n");
35524     }
35525 
35526     if(!disablelog)
35527     {
35528         printf("Release sound system.........");
35529     }
35530     if(startup_done)
35531     {
35532         sound_exit();
35533     }
35534     if(!disablelog)
35535     {
35536         printf("\tDone!\n");
35537     }
35538 
35539     if(!disablelog)
35540     {
35541         printf("Release FileCaching System...");
35542     }
35543     if(startup_done)
35544     {
35545         pak_term();
35546     }
35547     if(!disablelog)
35548     {
35549         printf("\tDone!\n");
35550     }
35551 
35552     if(modelcmdlist)
35553     {
35554         freeCommandList(modelcmdlist);    // moved here because list is not initialized if shutdown is initiated from inside the menu
35555     }
35556     if(modelstxtcmdlist)
35557     {
35558         freeCommandList(modelstxtcmdlist);
35559     }
35560     if(levelcmdlist)
35561     {
35562         freeCommandList(levelcmdlist);
35563     }
35564     if(levelordercmdlist)
35565     {
35566         freeCommandList(levelordercmdlist);
35567     }
35568 
35569     freeModelList();
35570     if(savelevel)
35571     {
35572         free(savelevel);
35573     }
35574     freefilenamecache();
35575     ob_termtrans();
35576 
35577     // free input recorder
35578     free_input_recorder();
35579 
35580     if(!disablelog)
35581     {
35582         printf("\n**************** Done *****************\n\n");
35583     }
35584 
35585     if(!disablelog)
35586     {
35587         printf("%s", buf);
35588     }
35589 
35590     shuttingdown = 0;
35591     borExit(status);
35592 }
35593 
35594 
35595 #if DC
guistartup()35596 void guistartup()
35597 {
35598     int i;
35599 
35600     if(!font_load(0, "menu/font1", packfile, 0))
35601     {
35602         borShutdown(1, "Unable to load font #1!\n");
35603     }
35604     if(!font_load(1, "menu/font2", packfile, 0))
35605     {
35606         borShutdown(1, "Unable to load font #2!\n");
35607     }
35608     if(!font_load(2, "menu/font3", packfile, 0))
35609     {
35610         borShutdown(1, "Unable to load font #3!\n");
35611     }
35612 
35613 
35614     borTimerInit();
35615 
35616     control_init(2);
35617     apply_controls();
35618 
35619     init_videomodes(0);
35620     if(!video_set_mode(videomodes))
35621     {
35622         borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
35623     }
35624 
35625     for(i = 0; i < 256; i++)
35626     {
35627         neontable[i] = i;
35628     }
35629 }
35630 #endif
35631 
startup()35632 void startup()
35633 {
35634     int i;
35635 
35636     printf("FileCaching System Init......\t");
35637     if(pak_init())
35638     {
35639         printf("Enabled\n");
35640     }
35641     else
35642     {
35643         printf("Disabled\n");
35644     }
35645 
35646 #if PSP
35647     if(savedata.pspcpuspeed < 0)
35648     {
35649         savedata.pspcpuspeed = 2;
35650     }
35651     if(savedata.pspcpuspeed > 2)
35652     {
35653         savedata.pspcpuspeed = 0;
35654     }
35655     switch(savedata.pspcpuspeed)
35656     {
35657     case 0:
35658         scePowerSetClockFrequency(222, 222, 111);
35659         break;
35660     case 1:
35661         scePowerSetClockFrequency(266, 266, 133);
35662         break;
35663     case 2:
35664         scePowerSetClockFrequency(333, 333, 166);
35665         break;
35666     }
35667 #endif
35668 
35669     ob_inittrans();
35670     loadHighScoreFile();
35671     clearSavedGame();
35672 
35673     init_videomodes(1);
35674     if(!video_set_mode(videomodes))
35675     {
35676         borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
35677     }
35678 
35679     printf("Loading menu.txt.............\t");
35680     load_menu_txt();
35681     printf("Done!\n");
35682 
35683     printf("Loading fonts................\t");
35684     load_all_fonts();
35685     printf("Done!\n");
35686 
35687     printf("Timer init...................\t");
35688     borTimerInit();
35689     printf("Done!\n");
35690 
35691     printf("Initialize Sound..............\t");
35692     if(sound_init(12))
35693     {
35694         if(load_special_sounds())
35695         {
35696             printf("Done!\n");
35697         }
35698         else
35699         {
35700             printf("\n");
35701         }
35702         if(!sound_start_playback())
35703         {
35704             printf("Warning: can't play sound!\n");
35705         }
35706         SB_setvolume(SB_MASTERVOL, 15);
35707         SB_setvolume(SB_VOICEVOL, savedata.soundvol);
35708     }
35709     else
35710     {
35711         borShutdown(1, "Unable to Initialize Sound.\n");
35712     }
35713 
35714     // init. input recorder
35715     init_input_recorder();
35716 
35717     printf("Loading sprites..............\t");
35718     load_special_sprites();
35719     printf("Done!\n");
35720 
35721     printf("Loading level order..........\t");
35722     load_levelorder();
35723     printf("Done!\n");
35724 
35725     printf("Loading model constants......\t");
35726     load_model_constants();
35727     printf("Done!\n");
35728 
35729     printf("Loading script settings......\t");
35730     load_script_setting();
35731     printf("Done!\n");
35732 
35733     printf("Loading scripts..............\t");
35734     load_scripts();
35735     printf("Done!\n");
35736 
35737     printf("Loading models...............\n\n");
35738     load_models();
35739 
35740     printf("Object engine init...........\t");
35741     if(!alloc_ents())
35742     {
35743         borShutdown(1, "Not enough memory for game objects!\n");
35744     }
35745     printf("Done!\n");
35746 
35747     printf("Input init...................\t");
35748     control_init(savedata.usejoy);
35749     apply_controls();
35750     printf("Done!\n");
35751 
35752 #ifdef CACHE_BACKGROUNDS
35753     printf("Caching backgrounds..........\t");
35754     cache_all_backgrounds();
35755     printf("Done!\n");
35756 #endif
35757 
35758     printf("Create blending tables.......\t");
35759     if(pixelformat == PIXEL_x8)
35760     {
35761         create_blend_tables_x8(blendtables);
35762     }
35763 
35764     for(i = 0; i < MAX_PAL_SIZE / 4; i++)
35765     {
35766         neontable[i] = i;
35767     }
35768     printf("Done!\n");
35769 
35770     if(savedata.logo++ > 10)
35771     {
35772         savedata.logo = 0;
35773     }
35774 
35775     printf("Save settings so far........\t");
35776     savesettings();
35777     printf("Done!\n");
35778 
35779     startup_done = 1;
35780 
35781     printf("\n\n");
35782 
35783 }
35784 
35785 
35786 
35787 // ----------------------------------------------------------------------------
35788 
35789 
35790 // Returns 0 on error, -1 on escape
playgif(char * filename,int x,int y,int noskip)35791 int playgif(char *filename, int x, int y, int noskip)
35792 {
35793     anigif_info *info = calloc(1, sizeof(*info));
35794     s_screen *backbuffer = NULL;
35795     s_screen *tempbg = background;
35796     int result = 1;
35797     u32 synctosound;
35798     u32 lasttime;
35799     u32 milliseconds;
35800     u32 temptime, tempnewtime; // temporary patch for ingame gif play
35801 
35802     synctosound = (sound_getinterval() != 0xFFFFFFFF);
35803     temptime = _time;
35804     tempnewtime = newtime;
35805     _time = 0;
35806     milliseconds = 0;
35807     lasttime = 0;
35808     background = NULL;
35809 
35810     if(!(result = anigif_open(filename, packfile, info)))
35811     {
35812         goto playgif_end;
35813     }
35814 
35815     while(!info->done)
35816     {
35817         if(milliseconds >= info->info[0].nextframe)
35818         {
35819             anigif_decode_frame(info);
35820         }
35821         if(!(backbuffer = anigif_getbuffer(info)))
35822         {
35823             break;
35824         }
35825         spriteq_add_screen(x, y, 0, backbuffer, NULL, 0);
35826         if(info->frame == 0)
35827         {
35828             vga_vwait();
35829             update(0, 0);
35830         }
35831         else
35832         {
35833             update(0, 1);
35834         }
35835 
35836         if(synctosound)
35837         {
35838             milliseconds += sound_getinterval();
35839         }
35840         else
35841         {
35842             milliseconds += (_time - lasttime) * 1000 / GAME_SPEED;
35843         }
35844 
35845         lasttime = _time;
35846 
35847         if(!noskip && (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)))
35848         {
35849             result = -1;
35850             break;
35851         }
35852     }
35853 
35854 
35855 playgif_end:
35856     anigif_close(info);
35857     free(info);
35858     _time = temptime;
35859     newtime = tempnewtime;
35860     background = tempbg;
35861     standard_palette(1);
35862 
35863     if(0 == result)
35864     {
35865         printf("\nWarning, an error occurred while playing animated gif file '%s'.\n", filename);
35866     }
35867     return result;
35868 
35869 }
35870 
35871 
35872 #ifdef WEBM
35873 // Returns 0 on error, -1 on escape
playwebm(const char * path,int noskip)35874 int playwebm(const char *path, int noskip)
35875 {
35876     int retval = 1;
35877     webm_context *ctx = NULL;
35878     yuv_video_mode info;
35879     s_screen *rgb_frame = NULL;
35880 
35881     ctx = webm_start_playback(path, savedata.musicvol);
35882     if(ctx == NULL) {retval=0; goto quit;}
35883 
35884     // set video output to YUV mode
35885     webm_get_video_info(ctx, &info);
35886     int status = video_setup_yuv_overlay(&info);
35887     if(!status) {retval=0; goto quit;}
35888 
35889     // allocate s_screen for screenshot capture
35890     yuv_init(2);
35891     rgb_frame = allocscreen(info.width, info.height, PIXEL_16);
35892     if(!rgb_frame) {retval=0; goto quit;}
35893 
35894     u64 start_time = timer_uticks();
35895     u64 next_frame_time = 0;
35896     yuv_frame *frame = NULL;
35897 
35898     while(1)
35899     {
35900         inputrefresh(playrecstatus->status);
35901         if(!noskip && (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)))
35902         {
35903             retval = -1;
35904             yuv_frame_destroy(frame);
35905             break;
35906         }
35907         else if(frame && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT))
35908         {
35909             yuv_to_rgb(frame, rgb_frame);
35910             screenshot(rgb_frame, NULL, 0);
35911         }
35912 
35913         u64 time_passed = timer_uticks() - start_time;
35914 
35915         if(next_frame_time <= time_passed)
35916         {
35917             // display the current frame
35918             if(frame)
35919             {
35920                 video_display_yuv_frame();
35921                 yuv_frame_destroy(frame);
35922             }
35923 
35924             // prepare the next frame for display
35925             frame = webm_get_next_frame(ctx);
35926             if(frame == NULL) break;
35927             video_prepare_yuv_frame(frame);
35928             next_frame_time = frame->timestamp / 1000;
35929         }
35930         else usleep(next_frame_time - time_passed);
35931     }
35932 
35933 quit:
35934     if(ctx) webm_close(ctx);
35935     if(rgb_frame) freescreen(&rgb_frame);
35936     yuv_clear();
35937     video_set_mode(videomodes);
35938     return retval;
35939 }
35940 #endif
35941 
35942 
35943 
playscene(char * filename)35944 void playscene(char *filename)
35945 {
35946     char *buf;
35947     size_t size;
35948     int pos;
35949     char *command = NULL;
35950     char videofile[MAX_BUFFER_LEN];
35951     int x = 0, y = 0, skipone = 0, noskip = 0, i;
35952     int closing = 0, status;
35953 
35954     ArgList arglist;
35955     char argbuf[MAX_ARG_LEN + 1] = "";
35956 
35957     // Read file
35958     if(buffer_pakfile(filename, &buf, &size) != 1)
35959     {
35960         return;
35961     }
35962 
35963     currentScene = filename;
35964 
35965     // Now interpret the contents of buf line by line
35966     pos = 0;
35967     while(buf[pos])
35968     {
35969         ParseArgs(&arglist, buf + pos, argbuf);
35970         command = GET_ARG(0);
35971         if(command[0])
35972         {
35973             if(!closing && stricmp(command, "music") == 0)
35974             {
35975                 music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
35976             }
35977             else if(!closing && stricmp(command, "animation") == 0)
35978             {
35979                 strcpy(videofile, GET_ARG(1));
35980                 x = GET_INT_ARG(2);
35981                 y = GET_INT_ARG(3);
35982                 skipone = GET_INT_ARG(4);
35983                 noskip = GET_INT_ARG(5);
35984                 status = playgif(videofile, x, y, noskip);
35985                 if(status == -1 && !skipone)
35986                 {
35987                     closing = 1;
35988                 }
35989             }
35990             else if(!closing && stricmp(command, "video") == 0)
35991             {
35992 #ifdef WEBM
35993                 strcpy(videofile, GET_ARG(1));
35994                 skipone = GET_INT_ARG(2);
35995                 noskip = GET_INT_ARG(3);
35996                 status = playwebm(videofile, noskip);
35997                 if(status == -1 && !skipone)
35998                 {
35999                     closing = 1;
36000                 }
36001                 else if(status == 0)
36002                 {
36003                     printf("An error occurred when trying to play the video %s\n", videofile);
36004                 }
36005 #else
36006                 printf("Skipping video %s; WebM playback not supported on this platform\n");
36007 #endif
36008             }
36009             else if(stricmp(command, "silence") == 0)
36010             {
36011                 sound_close_music();
36012             }
36013         }
36014         // Go to next non-blank line
36015         pos += getNewLineStart(buf + pos);
36016     }
36017     if(buf != NULL)
36018     {
36019         free(buf);
36020         buf = NULL;
36021     }
36022     currentScene = NULL;
36023     for(i = 0; i < MAX_PLAYERS; i++)
36024     {
36025         player[i].disablekeys = player[i].newkeys = player[i].playkeys = 0;
36026     }
36027 }
36028 
36029 
36030 
36031 
36032 // ----------------------------------------------------------------------------
36033 
36034 
36035 
36036 
gameover()36037 void gameover()
36038 {
36039     int done = 0;
36040     char tmpBuff[MAX_BUFFER_LEN] = {""};
36041 
36042     music("data/music/gameover", 0, 0);
36043 
36044     _time = 0;
36045     gameOver = 1;
36046 
36047     if(custScenes != NULL)
36048     {
36049         strcpy(tmpBuff, custScenes);
36050         strcat(tmpBuff, "gameover.txt");
36051         if(testpackfile(tmpBuff, packfile) >= 0)
36052         {
36053             playscene(tmpBuff);
36054             done = 1;
36055         }
36056     }
36057     else
36058     {
36059         if(testpackfile("data/scenes/gameover.txt", packfile) >= 0)
36060         {
36061             playscene("data/scenes/gameover.txt");
36062             done = 1;
36063         }
36064 
36065     }
36066 
36067     while(!done)
36068     {
36069         font_printf(_strmidx(3, Tr("GAME OVER")), 110 + videomodes.vShift, 3, 0, Tr("GAME OVER"));
36070         done |= (_time > GAME_SPEED * 8 && !sound_query_music(NULL, NULL));
36071         done |= (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON));
36072         update(0, 0);
36073     }
36074     gameOver = 0;
36075 }
36076 
36077 
36078 
36079 
hallfame(int addtoscore)36080 void hallfame(int addtoscore)
36081 {
36082     int done = 0;
36083     int topten[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
36084     u32 score;
36085     char name[MAX_NAME_LEN + 1];
36086     int i, p, y;
36087     char tmpBuff[MAX_BUFFER_LEN] = {""};
36088     int col1 = -8;
36089     int col2 = 6;
36090 
36091     hallOfFame = 1;
36092 
36093     if(hiscorebg)
36094     {
36095         // New alternative background path for PSP
36096         if(custBkgrds != NULL)
36097         {
36098             strcpy(tmpBuff, custBkgrds);
36099             strcat(tmpBuff, "hiscore");
36100             load_background(tmpBuff);
36101         }
36102         else
36103         {
36104             load_cached_background("data/bgs/hiscore");
36105         }
36106     }
36107 
36108     if(addtoscore)
36109     {
36110         for(p = 0; p < levelsets[current_set].maxplayers; p++)
36111         {
36112             if(player[p].score > savescore.highsc[9])
36113             {
36114                 savescore.highsc[9] = player[p].score;
36115                 strcpy(savescore.hscoren[9], player[p].name);
36116                 topten[9] = 1;
36117 
36118                 for(i = 8; i >= 0 && player[p].score > savescore.highsc[i]; i--)
36119                 {
36120                     score = savescore.highsc[i];
36121                     strcpy(name, savescore.hscoren[i]);
36122                     savescore.highsc[i] = player[p].score;
36123                     strcpy(savescore.hscoren[i], player[p].name);
36124                     topten[i] = 1;
36125                     savescore.highsc[i + 1] = score;
36126                     strcpy(savescore.hscoren[i + 1], name);
36127                     topten[i + 1] = 0;
36128                 }
36129             }
36130         }
36131         saveHighScoreFile();
36132     }
36133 
36134     _time = 0;
36135 
36136     while(!done)
36137     {
36138         y = 56;
36139         if(!hiscorebg)
36140         {
36141             font_printf(_strmidx(3, Tr("Hall Of Fame")), y - fontheight(3) - 10 + videomodes.vShift, 3, 0, Tr("Hall Of Fame"));
36142         }
36143 
36144         for(i = 0; i < 10; i++)
36145         {
36146             font_printf(_colx(topten[i], col1), y + videomodes.vShift, topten[i], 0, "%2i.  %s", i + 1, savescore.hscoren[i]);
36147             font_printf(_colx(topten[i], col2), y + videomodes.vShift, topten[i], 0, (scoreformat ? "%09lu" : "%u"), savescore.highsc[i]);
36148             y += (videomodes.vRes - videomodes.vShift - 56 - 32) / 10; //font_heights[topten[i]] + 6;
36149         }
36150 
36151         update(0, 0);
36152         done |= (_time > GAME_SPEED * 8);
36153         done |= (bothnewkeys & (FLAG_START + FLAG_ESC));
36154     }
36155     unload_background();
36156     hallOfFame = 0;
36157 }
36158 
36159 
36160 
36161 
36162 // Level completed, show bonus stuff
showcomplete(int num)36163 void showcomplete(int num)
36164 {
36165     int done = 0;
36166     int i, j, k;
36167     u32 clearbonus[4] = { 10000, 10000, 10000, 10000 };
36168     u32 lifebonus[4] = { 10000, 10000, 10000, 10000 };
36169     u32 rushbonus[4] = { 10000, 10000, 10000, 10000 };
36170     u32 nexttime = 0;
36171     u32 finishtime = 0;
36172     int chan = 0;
36173     char tmpBuff[MAX_BUFFER_LEN] = {""};
36174 
36175     showComplete = 1;
36176 
36177     if(completebg)
36178     {
36179         // New alternative background path for PSP
36180         if(custBkgrds != NULL)
36181         {
36182             strcpy(tmpBuff, custBkgrds);
36183             strcat(tmpBuff, "complete");
36184             load_background(tmpBuff);
36185         }
36186         else
36187         {
36188             load_cached_background("data/bgs/complete");
36189         }
36190     }
36191 
36192     music("data/music/complete", 0, 0);
36193 
36194     for(i = 0; i < levelsets[current_set].maxplayers; i++)
36195     {
36196         if(rush[0] >= 1 && showrushbonus == 1)
36197         {
36198             rushbonus[i] = nomaxrushreset[i] * scbonuses[2];
36199         }
36200         if(scbonuses[3] == 1)
36201         {
36202             clearbonus[i] = num * scbonuses[0];
36203         }
36204         else
36205         {
36206             clearbonus[i] = scbonuses[0];
36207         }
36208         lifebonus[i] = player[i].lives * scbonuses[1];
36209     }
36210 
36211     update(0, 0);
36212 
36213     _time = 0;
36214     while(!done)
36215     {
36216         if(!scomplete[5])
36217         {
36218             font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, Tr("Stage %i Complete!"), num);
36219         }
36220         else
36221         {
36222             font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, Tr("Stage"));
36223             font_printf(videomodes.hShift + scomplete[2], videomodes.vShift + scomplete[3], 3, 0, "%i", num);
36224             font_printf(videomodes.hShift + scomplete[4], videomodes.vShift + scomplete[5], 3, 0, Tr("Complete"));
36225         }
36226 
36227         font_printf(videomodes.hShift + cbonus[0], videomodes.vShift + cbonus[1], 0, 0, Tr("Clear Bonus"));
36228         for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
36229             {
36230                 font_printf(videomodes.hShift + cbonus[j], videomodes.vShift + cbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), clearbonus[i]);
36231             }
36232         font_printf(videomodes.hShift + lbonus[0], videomodes.vShift + lbonus[1], 0, 0, Tr("Life bonus"));
36233         for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
36234             {
36235                 font_printf(videomodes.hShift + lbonus[j], videomodes.vShift + lbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), lifebonus[i]);
36236             }
36237         if(rush[0] >= 1 && showrushbonus == 1)
36238         {
36239             font_printf(videomodes.hShift + rbonus[0], videomodes.vShift + rbonus[1], 0, 0, Tr("Rush Bonus"));
36240             for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
36241                 {
36242                     font_printf(videomodes.hShift + rbonus[j], videomodes.vShift + rbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), rushbonus[i]);
36243                 }
36244         }
36245         font_printf(videomodes.hShift + tscore[0], videomodes.vShift + tscore[1], 0, 0, Tr("Total Score"));
36246         for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
36247             {
36248                 font_printf(videomodes.hShift + tscore[j], videomodes.vShift + tscore[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), player[i].score);
36249             }
36250 
36251         while(_time > nexttime)
36252         {
36253             if(!finishtime)
36254             {
36255                 finishtime = _time + 4 * GAME_SPEED;
36256             }
36257 
36258             for(i = 0; i < levelsets[current_set].maxplayers; i++)
36259             {
36260                 if(player[i].lives > 0)
36261                 {
36262                     if(clearbonus[i] > 0)
36263                     {
36264                         addscore(i, 10);
36265                         clearbonus[i] -= 10;
36266                         finishtime = 0;
36267                     }
36268                     else if(lifebonus[i] > 0)
36269                     {
36270                         addscore(i, 10);
36271                         lifebonus[i] -= 10;
36272                         finishtime = 0;
36273                     }
36274                     else if(rush[0] >= 1 && showrushbonus == 1 && (rushbonus[i] > 0))
36275                     {
36276                         addscore(i, 10);
36277                         rushbonus[i] -= 10;
36278                         finishtime = 0;
36279                     }
36280                 }
36281             }
36282 
36283             if(!finishtime && !(nexttime & 15))
36284             {
36285                 sound_stop_sample(chan);
36286                 chan = sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol / 2, savedata.effectvol / 2, 100);
36287             }
36288             nexttime++;
36289         }
36290 
36291         if(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC))
36292         {
36293             done = 1;
36294         }
36295         if(finishtime && _time > finishtime)
36296         {
36297             done = 1;
36298         }
36299 
36300         update(0, 0);
36301     }
36302 
36303     // Add remainder of score, incase player skips counter
36304     for(i = 0; i < levelsets[current_set].maxplayers; i++)
36305     {
36306         if(player[i].lives > 0)
36307         {
36308             if(rush[0] >= 1 && showrushbonus == 1)
36309             {
36310                 addscore(i, rushbonus[i]);
36311             }
36312             addscore(i, clearbonus[i]);
36313             addscore(i, lifebonus[i]);
36314         }
36315     }
36316     unload_background();
36317 
36318     showComplete = 0;
36319 }
36320 
savelevelinfo()36321 void savelevelinfo()
36322 {
36323     int i;
36324     s_set_entry *set = levelsets + current_set;
36325     s_savelevel *save = savelevel + current_set;
36326 
36327     save->flag = set->saveflag;
36328     // don't check flag here save all info, for simple logic
36329     for(i = 0; i < set->maxplayers; i++)
36330     {
36331         save->pLives[i] = player[i].lives;
36332         save->pCredits[i] = player[i].credits;
36333         save->pScores[i] = player[i].score;
36334         save->pSpawnhealth[i] = player[i].spawnhealth;
36335         save->pSpawnmp[i] = player[i].spawnmp;
36336         save->pWeapnum[i] = player[i].weapnum;
36337         save->pColourmap[i] = player[i].colourmap;
36338         strncpy(save->pName[i], player[i].name, MAX_NAME_LEN);
36339     }
36340     save->credits = credits;
36341     save->level = current_level;
36342     save->stage = current_stage;
36343     save->which_set = current_set;
36344     strncpy(save->dName, set->name, MAX_NAME_LEN - 1);
36345     for(i = 0; i < sizeof(allowselect_args); i++) save->allowSelectArgs[i] = '\0'; // clear
36346     for(i = 0; i < sizeof(allowselect_args); i++) save->allowSelectArgs[i] = allowselect_args[i];
36347 }
36348 
tryvictorypose(entity * ent)36349 void tryvictorypose(entity *ent)
36350 {
36351     if( ent &&
36352        !ent->inpain &&
36353        !ent->falling &&
36354        !ent->dead &&
36355        !ent->rising &&
36356        (ent->idling & IDLING_ACTIVE) &&
36357        ent->position.y <= ent->base )
36358     {
36359         ent->takeaction = NULL;
36360         ent->velocity.x = ent->velocity.z = 0;
36361         ent->idling = IDLING_NONE;
36362         ent_set_anim(ent, ANI_VICTORY, 0);
36363     }
36364 }
36365 
check_victory_pose()36366 static void check_victory_pose()
36367 {
36368     if ( (endgame & 1) && level_completed_defeating_boss )
36369     {
36370         int i;
36371         for(i = 0; i < MAX_PLAYERS; i++)
36372         {
36373             entity* tmp = self;
36374             self = player[i].ent;
36375             if(self && validanim(self, ANI_VICTORY) && self->animnum != ANI_VICTORY)
36376             {
36377                 tryvictorypose(self);
36378                 endgame = 0;
36379             }
36380             else if (self && self->animnum == ANI_VICTORY && self->animating)
36381             {
36382                 endgame = 0;
36383             }
36384 
36385             self = tmp;
36386         }
36387     }
36388 }
36389 
playlevel(char * filename)36390 int playlevel(char *filename)
36391 {
36392     int i, type, p_alive = 0;
36393 
36394     kill_all();
36395 
36396     savelevelinfo(); // just in case we lose them after level is freed
36397 
36398     load_level(filename);
36399 
36400     if(!nosave)
36401     {
36402         saveGameFile();
36403         saveHighScoreFile();
36404         saveScriptFile();
36405     }
36406     nosave = 0;
36407 
36408     _time = 0;
36409     nextplan = 0;
36410     stalker = NULL;
36411     firstplayer = NULL;
36412     type = level->type;
36413 
36414     // Fixes the start level executing last button bug
36415     for(i = 0; i < levelsets[current_set].maxplayers; i++)
36416     {
36417         if(player[i].lives > 0)
36418         {
36419             player[i].disablekeys = player[i].newkeys = player[i].playkeys = 0;
36420             player[i].weapnum = level->setweap;
36421             player[i].joining = 0;
36422             player[i].hasplayed = 1;
36423             spawnplayer(i);
36424             player[i].ent->rush.count.max = 0;
36425         }
36426     }
36427 
36428     //execute a script when level started
36429     if(Script_IsInitialized(&level_script))
36430     {
36431         Script_Execute(&level_script);
36432     }
36433     if(Script_IsInitialized(&(level->level_script)))
36434     {
36435         Script_Execute(&(level->level_script));
36436     }
36437 
36438     while(!endgame)
36439     {
36440         update(1, 0);
36441 
36442         if (level->force_finishlevel)
36443         {
36444             level_completed = 1;
36445             endgame |= 1;
36446             level->force_finishlevel = 0;
36447         }
36448         if (level->force_gameover)
36449         {
36450             //entity* temp = self;
36451             for(i = 0; i < MAX_PLAYERS; i++) //levelsets[current_set].maxplayers
36452             {
36453                 player[i].lives = 0;
36454                 if(noshare)
36455                 {
36456                     player[i].credits = 0;
36457                 }
36458                 else
36459                 {
36460                     credits = 0;
36461                 }
36462                 if (player[i].ent)
36463 				{
36464 					kill_entity(player[i].ent);
36465 					player[i].ent = NULL;
36466 				}
36467                 //self = player[i].ent;
36468                 //player_die();
36469             }
36470             //self = temp;
36471             //kill_all();
36472             endgame |= 1;
36473             level->force_gameover = 0;
36474         }
36475         if(level_completed)
36476         {
36477             endgame |= (!findent(TYPE_ENEMY) || level->type || findent(TYPE_ENDLEVEL));    // Ends when all enemies die or a bonus level
36478             check_victory_pose();
36479         }
36480     }
36481     //execute a script when level finished
36482     if(Script_IsInitialized(&endlevel_script))
36483     {
36484         Script_Execute(&endlevel_script);
36485     }
36486     if(Script_IsInitialized(&(level->endlevel_script)))
36487     {
36488         Script_Execute(&(level->endlevel_script));
36489     }
36490     if(!nofadeout)
36491     {
36492         fade_out(0, 0);
36493     }
36494 
36495     for(i = 0; i < levelsets[current_set].maxplayers; i++)
36496     {
36497         if(player[i].ent)
36498         {
36499             nomaxrushreset[i] = player[i].ent->rush.count.max;
36500             player[i].spawnhealth = player[i].ent->energy_state.health_current;
36501             player[i].spawnmp = player[i].ent->energy_state.mp_current;
36502         }
36503         // reset
36504         player[i].weapnum = 0;
36505     }
36506 
36507     if(!musicoverlap)
36508     {
36509         sound_close_music();
36510     }
36511     sound_stopall_sample();
36512 
36513     unload_level();
36514 
36515     // Are any players alive?
36516 	for(i = 0; i < MAX_PLAYERS; i++)
36517     {
36518         if (player[i].lives > 0)
36519         {
36520             p_alive = 1;
36521             break;
36522         }
36523     }
36524 
36525     return ((type == 2 && endgame != 2) || p_alive);
36526 }
36527 
36528 // Caskey, Damon V (retool, OA unknown)
36529 // 2019-01-03
36530 //
36531 // For select screen. Spawn sample entity for player_index.
spawnexample(int player_index)36532 static entity *spawnexample(int player_index)
36533 {
36534 	#define SPAWN_MODEL_NAME	NULL
36535 	#define SPAWN_MODEL_INDEX	-1
36536 
36537     entity	*example;
36538 	s_model *model;
36539 	s_set_entry *set;
36540 
36541 	float pos_x;
36542 	float pos_y;
36543 	float pos_z;
36544 	int direction;
36545 
36546 	set = levelsets + current_set;
36547 
36548 	// Get spawn attributes and spawn entity.
36549 	pos_x = (float)psmenu[player_index][0];
36550 	pos_y = 0;
36551 	pos_z = (float)psmenu[player_index][1];
36552 	direction = spdirection[player_index];
36553 
36554 	// Next selectable model in cycle. We'll use this
36555 	// to decide what we spawn.
36556 	model = nextplayermodeln(NULL, player_index);
36557 
36558 	example = spawn(pos_x, pos_z, pos_y, direction, SPAWN_MODEL_NAME, SPAWN_MODEL_INDEX, model);
36559 
36560 	// Copy model's name to player property.
36561 	strcpy(player[player_index].name, model->name);
36562 
36563 	// If color selection is allowed and we want
36564 	// players with same models to use different
36565 	// maps, then use next map in cycle. Otherwise
36566 	// just go with default map.
36567 	if (colourselect && (set->nosame & 2))
36568 	{
36569 		player[player_index].colourmap = nextcolourmapn(model, -1, player_index);
36570 	}
36571 	else
36572 	{
36573 		player[player_index].colourmap = 0;
36574 	}
36575 
36576 	// Apply map to spawned entity.
36577     ent_set_colourmap(example, player[player_index].colourmap);
36578 
36579 	// So the entity knows how it came to be.
36580 	example->spawntype = SPAWN_TYPE_PLAYER_SELECT;
36581 
36582 	return example;
36583 
36584 	#undef SPAWN_MODEL_NAME
36585 	#undef SPAWN_MODEL_INDEX
36586 }
36587 
36588 // load saved select screen
load_select_screen_info(s_savelevel * save)36589 static void load_select_screen_info(s_savelevel *save)
36590 {
36591     int i = 0;
36592     ArgList arglist;
36593     char argbuf[MAX_ARG_LEN + 1] = "";
36594     char *filename = ""; // not used, just fused into GET_INT_ARG()
36595     char *command = ""; // not used, just fused into GET_INT_ARG()
36596 
36597     // load model_loads
36598     for(i = 0; i < save->selectLoadCount; i++)
36599     {
36600         s_model *tempmodel;
36601         command = GET_ARG(0);
36602 
36603         if(!command || !command[0]) continue;
36604         ParseArgs(&arglist, save->selectLoad[i], argbuf);
36605 
36606         tempmodel = findmodel(GET_ARG(1));
36607         if (tempmodel)
36608         {
36609             update_model_loadflag(tempmodel, GET_INT_ARG(2));
36610         }
36611     }
36612 
36613     ParseArgs(&arglist, save->selectMusic, argbuf);
36614     command = GET_ARG(0);
36615     if(command && command[0]) music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
36616 
36617     ParseArgs(&arglist, save->selectBackground, argbuf);
36618     command = GET_ARG(0);
36619     if(command && command[0]) load_background(GET_ARG(1));
36620 
36621     return;
36622 }
36623 
selectplayer(int * players,char * filename,int useSavedGame)36624 int selectplayer(int *players, char *filename, int useSavedGame)
36625 {
36626 	s_model *tempmodel;
36627 	s_model *model_old = NULL;
36628 	s_model *model_new = NULL;
36629 	int i;
36630 	int exit = 0;
36631 	int escape = 0;
36632 	int defaultselect = 0;
36633 	unsigned exitdelay = 0;
36634 	int players_busy = 0;
36635 	int players_ready = 0;
36636 	char string[MAX_BUFFER_LEN] = { "" };
36637 	char *buf, *command;
36638 	size_t size = 0;
36639 	ptrdiff_t pos = 0;
36640 	ArgList arglist;
36641 	char argbuf[MAX_ARG_LEN + 1] = "";
36642 	s_set_entry *set = levelsets + current_set;
36643 	s_savelevel *save = savelevel + current_set;
36644 	int load_count = 0;
36645 	int saved_select_screen = 0;
36646 	int is_first_select = 1;
36647 
36648 	savelevelinfo();
36649 
36650 	selectScreen = 1;
36651 	kill_all();
36652 
36653 	// Initialize player sized arrays.
36654 	entity *example[set->maxplayers];
36655 	int ready[set->maxplayers];
36656 
36657 	// Initialize
36658 	for (i = 0; i < set->maxplayers; i++)
36659 	{
36660 		example[i] = NULL;
36661 		ready[i] = 0;
36662 	}
36663 
36664 	// Allow select? 'a' is the first char of allowselect,
36665 	// if there's 'a' then there is allowselect.
36666 	if (allowselect_args[0] != 'a'
36667 		&& allowselect_args[0] != 'A')
36668 	{
36669 		reset_playable_list(1);
36670 	}
36671 
36672 	// Reset memory for player array.
36673 	memset(player, 0, sizeof(*player) * MAX_PLAYERS);
36674 
36675 	// Load game selected and a save game available?
36676 	if (useSavedGame && save)
36677 	{
36678 		if (save->selectFlag)
36679 		{
36680 			load_select_screen_info(save);
36681 			load_playable_list(save->allowSelectArgs);
36682 			saved_select_screen = 1;
36683 		}
36684 	}
36685 
36686 	// Mark "hasplayed" for all players.
36687 	for (i = 0; i < set->maxplayers; i++)
36688 	{
36689 		player[i].hasplayed = players[i];
36690 	}
36691 
36692 	for (i = 0; i < set->maxplayers; i++)
36693 	{
36694 		if (savelevel[current_set].pLives[i] > 0)
36695 		{
36696 			is_first_select = 0;
36697 			break;
36698 		}
36699 	}
36700 
36701 	// Load and apply selection text file.
36702 	if (filename && filename[0])
36703 	{
36704 		if (buffer_pakfile(filename, &buf, &size) != 1)
36705 		{
36706 			borShutdown(1, "Failed to load player select file '%s'", filename);
36707 		}
36708 		while (pos < size)
36709 		{
36710 			ParseArgs(&arglist, buf + pos, argbuf);
36711 			command = GET_ARG(0);
36712 			if (command && command[0])
36713 			{
36714 				if (stricmp(command, "music") == 0)
36715 				{
36716 					music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
36717 					// SAVE
36718 					multistrcatsp(save->selectMusic, command, GET_ARG(1), GET_ARG(2), GET_ARG(3), NULL);
36719 				}
36720 				else if (stricmp(command, "allowselect") == 0)
36721 				{
36722 					load_playable_list(buf + pos);
36723 					memcpy(&save->allowSelectArgs, &allowselect_args, sizeof(allowselect_args)); // SAVE
36724 				}
36725 				else if (stricmp(command, "background") == 0)
36726 				{
36727 					load_background(GET_ARG(1));
36728 					// SAVE
36729 					multistrcatsp(save->selectBackground, command, GET_ARG(1), NULL);
36730 				}
36731 				else if (stricmp(command, "load") == 0)
36732 				{
36733 					tempmodel = findmodel(GET_ARG(1));
36734 					if (!tempmodel)
36735 					{
36736 						load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2));
36737 					}
36738 					else
36739 					{
36740 						update_model_loadflag(tempmodel, GET_INT_ARG(2));
36741 					}
36742 					// SAVE
36743 					if (load_count < MAX_SELECT_LOADS)
36744 					{
36745 						multistrcatsp(save->selectLoad[load_count], command, GET_ARG(1), GET_ARG(2), NULL);
36746 						load_count++;
36747 					}
36748 				}
36749 				else if (command && command[0])
36750 				{
36751 					printf("Command '%s' is not understood in file '%s'\n", command, filename);
36752 				}
36753 			}
36754 
36755 			pos += getNewLineStart(buf + pos);
36756 		}
36757 		save->selectLoadCount = load_count; // SAVE number of LOAD command
36758 		save->selectFlag = 1;
36759 
36760 		if (buf != NULL)
36761 		{
36762 			free(buf);
36763 			buf = NULL;
36764 		}
36765 	}
36766 	else // without select.txt
36767 	{
36768 		if (is_first_select || (!skipselect[0][0] && !set->noselect)) // no select is skipselect without names
36769 		{
36770 			defaultselect = 1; // normal select or skipselect/noselect? 1 == normal select
36771 		}
36772 
36773 		if (!noshare)
36774 		{
36775 			credits = CONTINUES;
36776 		}
36777 		else for (i = 0; i < set->maxplayers; i++)
36778 		{
36779 			if (players[i])
36780 			{
36781 				player[i].credits = CONTINUES;
36782 			}
36783 		}
36784 
36785 		if (skipselect[0][0] || set->noselect)
36786 		{
36787 			for (i = 0; i < set->maxplayers; i++)
36788 			{
36789 				if (!players[i])
36790 				{
36791 					continue;
36792 				}
36793 				strncpy(player[i].name, skipselect[i], MAX_NAME_LEN);
36794 
36795 				if (defaultselect)
36796 				{
36797 					player[i].lives = PLAYER_LIVES;
36798 					if (!creditscheat)
36799 					{
36800 						if (noshare)
36801 						{
36802 							--player[i].credits;
36803 						}
36804 						else
36805 						{
36806 							--credits;
36807 						}
36808 					}
36809 				}
36810 				else
36811 				{
36812 					player[i].lives = savelevel[current_set].pLives[i];
36813 					player[i].score = savelevel[current_set].pScores[i];
36814 					if (noshare) player[i].credits = savelevel[current_set].pCredits[i];
36815 					else credits = savelevel[current_set].credits;
36816 				}
36817 			}
36818 			selectScreen = 0;
36819 
36820 			return 1;
36821 		}
36822 
36823 		if (!saved_select_screen)
36824 		{
36825 			if (unlockbg && bonus)
36826 			{
36827 				// New alternative background path for PSP
36828 				if (custBkgrds != NULL)
36829 				{
36830 					strcpy(string, custBkgrds);
36831 					strcat(string, "unlockbg");
36832 					load_background(string);
36833 				}
36834 				else
36835 				{
36836 					load_cached_background("data/bgs/unlockbg");
36837 				}
36838 			}
36839 			else
36840 			{
36841 				// New alternative background path for PSP
36842 				if (custBkgrds != NULL)
36843 				{
36844 					strcpy(string, custBkgrds);
36845 					strcat(string, "select");
36846 					load_background(string);
36847 				}
36848 				else
36849 				{
36850 					load_cached_background("data/bgs/select");
36851 				}
36852 			}
36853 			if (!music("data/music/menu", 1, 0))
36854 			{
36855 				music("data/music/remix", 1, 0);
36856 			}
36857 		}
36858 	}
36859 
36860 	for (i = 0; i < set->maxplayers; i++)
36861 	{
36862 		if (players[i])
36863 		{
36864 			example[i] = spawnexample(i);
36865 			player[i].playkeys = 0;
36866 			if (defaultselect)
36867 			{
36868 				player[i].lives = PLAYER_LIVES;
36869 				if (!creditscheat)
36870 				{
36871 					if (noshare)
36872 					{
36873 						--player[i].credits;
36874 					}
36875 					else
36876 					{
36877 						--credits;
36878 					}
36879 				}
36880 			}
36881 			else
36882 			{
36883 				player[i].lives = savelevel[current_set].pLives[i];
36884 				player[i].score = savelevel[current_set].pScores[i];
36885 				if (noshare) player[i].credits = savelevel[current_set].pCredits[i];
36886 				else credits = savelevel[current_set].credits;
36887 			}
36888 		}
36889 	}
36890 
36891 	_time = 0;
36892 
36893 	// Stay in selection until escape or exit.
36894 	//
36895 	// exit = all players ready (selected) and exit delay expired.
36896 	// escape = Escape key pressed.
36897 	while (!(exit || escape))
36898 	{
36899 		players_busy = 0;
36900 		players_ready = 0;
36901 
36902 		// Loop through players.
36903 		for (i = 0; i < set->maxplayers; i++)
36904 		{
36905 			// Current player index not yet selected?
36906 			if (!ready[i])
36907 			{
36908 
36909 				// This is where we present player selections. The logic is long
36910 				// and a little messy, so buckle up! Basically, we want to spawn
36911 				// an example entity to get started, and that example entity
36912 				// is what player sees on the selection screen. Then we switch
36913 				// its model/color/animation based on the situation and player
36914 				// input.
36915 				//
36916 				// 1. If an example entity exists and is playing a transition
36917 				//  then we...
36918 				//
36919 				// a) do nothing if the animation isn't finished.
36920 				//
36921 				// b) If it IS finished...
36922 				//
36923 				// -- 1. If the animation is ANI_SELECTIN, then play ANI_SELECT.
36924 				//
36925 				// -- 2. If aniamton is ANI_SELECTOUT, then we switch to new
36926 				// model (if available).
36927 				//
36928 				// 2. If player hasn't played yet, has some credits or
36929 				// can draw from credit pool and pressed any action button,
36930 				// then we'll deal with their credit pool and spawn the
36931 				// first example (selectable model preview). Having an
36932 				// example spawned also tells us the player has completed
36933 				// this step, and so it's OK to run actions from any of
36934 				// the others.
36935 				//
36936 				// 3. If the player pressed Left or Right instead and there's
36937 				// an example spawned, then we find the previous/next
36938 				// character in line, and record it to a variable. Then we
36939 				// see if example has ANI_SELECTIN. if it doesn't we switch
36940 				// to new model. If it does, play ANI_SELECT.
36941 				//
36942 				// 4. If the player presses Up or Down, we have an example
36943 				// spawned and colourselect is enabled, then cycle to the
36944 				// model's previous/next color set choice.
36945 				//
36946 				// 5. If the player presses any action button and we have
36947 				// an example spawned, then we mark the player's ready delay
36948 				// flag, and stalltime. See the parent logic block for
36949 				// selection delay & exit details. This is the player
36950 				// making their selection choice.
36951 
36952 				// Example exists and select transition animation?
36953 				if (example[i]
36954 					&& (example[i]->animnum == ANI_SELECTIN || example[i]->animnum == ANI_SELECTOUT))
36955 				{
36956 					// If still animating than do nothing. Let the transition finish.
36957 					if (example[i]->animating)
36958 					{
36959 					}
36960 					else
36961 					{
36962 						// Transition to select animation.
36963 						if (example[i]->animnum == ANI_SELECTIN)
36964 						{
36965 							ent_set_anim(example[i], ANI_SELECT, 0);
36966 						}
36967 
36968 						// Transition from select (player selected another model, and the
36969 						// select out transition is now finished). Repeat of left/right key
36970 						// logic below and probably needs consolidation.
36971 						if (example[i]->animnum == ANI_SELECTOUT && model_new)
36972 						{
36973 							// Apply new model.
36974 							ent_set_model(example[i], model_new->name, 0);
36975 
36976 							// Copy example model name to player name variable.
36977 							strcpy(player[i].name, example[i]->model->name);
36978 
36979 							// If colorselect is enabled and nosame 2 is enabled, skip to
36980 							// start at next avaialble color cycle. Otherwise just start
36981 							// with default color set (0).
36982 							if (colourselect && (set->nosame & 2))
36983 							{
36984 								player[i].colourmap = nextcolourmapn(example[i]->model, -1, i);
36985 							}
36986 							else
36987 							{
36988 								player[i].colourmap = 0;
36989 							}
36990 
36991 							//  Apply color set.
36992 							ent_set_colourmap(example[i], player[i].colourmap);
36993 						}
36994 					}
36995 				}
36996 				else if (!player[i].hasplayed
36997 					&& (noshare || credits > 0)
36998 					&& (player[i].newkeys & FLAG_ANYBUTTON))
36999 				{
37000 
37001 					//  Now this player has played.
37002 					players[i] = player[i].hasplayed = 1;
37003 					//printf("%d %d %d\n", i, player[i].lives, immediate[i]);
37004 
37005 					// Noshare means each player has their own credit pool.
37006 					if (noshare)
37007 					{
37008 						player[i].credits = CONTINUES;
37009 					}
37010 
37011 					// Credits cheat = infinite credits. If that's not enabled,
37012 					// then deduct a credit.
37013 					if (!creditscheat)
37014 					{
37015 						if (noshare)
37016 						{
37017 							--player[i].credits;
37018 						}
37019 						else
37020 						{
37021 							--credits;
37022 						}
37023 					}
37024 
37025 					// Give player default number of lives, spawn
37026 					// example model and cancel the key flag.
37027 					player[i].lives = PLAYER_LIVES;
37028 					example[i] = spawnexample(i);
37029 					player[i].playkeys = 0;
37030 
37031 					// Play sound effect.
37032 					if (SAMPLE_BEEP >= 0)
37033 					{
37034 						sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37035 					}
37036 				}
37037 				else if ((player[i].newkeys & FLAG_ANYBUTTON) && example[i]) //Kratus (01-05-21) Moved the "anybutton" code to before of the "left/right" code to fix a bug that makes no character chosen when both are pressed together
37038 				{
37039 					if (SAMPLE_BEEP2 >= 0)
37040 					{
37041 						sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
37042 					}
37043 					// yay you picked me!
37044 					if (validanim(example[i], ANI_PICK))
37045 					{
37046 						ent_set_anim(example[i], ANI_PICK, 0);
37047 					}
37048 					example[i]->stalltime = _time + GAME_SPEED * 2;
37049 					ready[i] = 1;
37050 				}
37051 				else if (player[i].newkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT) && example[i])
37052 				{
37053 					// Give player a feedback sound.
37054 					if (SAMPLE_BEEP >= 0)
37055 					{
37056 						sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37057 					}
37058 
37059 					// Get model in use right now.
37060 					model_old = example[i]->model;
37061 
37062 					// Let's get the new model. Left key = previous model
37063 					// in cycle. Right key = next.
37064 					if ((player[i].newkeys & FLAG_MOVELEFT))
37065 					{
37066 						model_new = prevplayermodeln(model_old, i);
37067 					}
37068 					else
37069 					{
37070 						model_new = nextplayermodeln(model_old, i);
37071 					}
37072 
37073 					// Do we have a select out transition? If so play it here.
37074 					// Otherwise switch to new model.
37075 					if (validanim(example[i], ANI_SELECTOUT))
37076 					{
37077 						ent_set_anim(example[i], ANI_SELECTOUT, 0);
37078 					}
37079 					else
37080 					{
37081 						// Apply new model.
37082 						ent_set_model(example[i], model_new->name, 0);
37083 
37084 						// Copy example model name to player name variable.
37085 						strcpy(player[i].name, example[i]->model->name);
37086 
37087 						// If colorselect is enabled and nosame 2 is enabled, skip to
37088 						// start at next avaialble color cycle. Otherwise just start
37089 						// with default color set (0).
37090 						if (colourselect && (set->nosame & 2))
37091 						{
37092 							player[i].colourmap = nextcolourmapn(example[i]->model, -1, i);
37093 						}
37094 						else
37095 						{
37096 							player[i].colourmap = 0;
37097 						}
37098 
37099 						//  Apply color set.
37100 						ent_set_colourmap(example[i], player[i].colourmap);
37101 					}
37102 				}
37103 				else if (player[i].newkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN) && colourselect && example[i])
37104 				{
37105 					player[i].colourmap = ((player[i].newkeys & FLAG_MOVEUP) ? nextcolourmapn : prevcolourmapn)(example[i]->model, player[i].colourmap, i);
37106 					ent_set_colourmap(example[i], player[i].colourmap);
37107 				}
37108 			}
37109 			else if (ready[i] == 1)
37110 			{
37111 				if (((!validanim(example[i], ANI_PICK) || example[i]->modeldata.animation[ANI_PICK]->loop.mode) && _time > example[i]->stalltime) || !example[i]->animating)
37112 				{
37113 					ready[i] = 2;
37114 					exitdelay = _time + GAME_SPEED;
37115 				}
37116 			}
37117 			else if (ready[i] == 2)
37118 			{
37119 				font_printf(psmenu[i][2], psmenu[i][3], 0, 0, Tr("Ready!"));
37120 			}
37121 
37122 			if (example[i] != NULL)
37123 			{
37124 				players_busy++;
37125 			}
37126 			if (ready[i] == 2)
37127 			{
37128 				players_ready++;
37129 			}
37130 		}
37131 
37132 		if (players_busy && players_busy == players_ready && exitdelay && _time > exitdelay)
37133 		{
37134 			exit = 1;
37135 		}
37136 		update(0, 0);
37137 
37138 		if (bothnewkeys & FLAG_ESC || escape_flag == 11) //Kratus (20-04-21) Added the new "escape" flag in the select screen by using the "gotomainmenu" function and the flag "11"
37139 		{
37140 			escape = 1;
37141             escape_flag = 0;
37142 		}
37143 	}
37144 
37145 	// No longer at the select screen
37146 	kill_all();
37147 	sound_close_music();
37148 	selectScreen = 0;
37149 
37150 	return (!escape);
37151 }
37152 
playgame(int * players,unsigned which_set,int useSavedGame)37153 void playgame(int *players,  unsigned which_set, int useSavedGame)
37154 {
37155     int i;
37156     current_level = 0;
37157     current_stage = 1;
37158     current_set = which_set;
37159     s_set_entry *set = levelsets + current_set;
37160     s_savelevel *save = savelevel + current_set;
37161     s_level_entry *le;
37162 
37163     useSave = 0;
37164     useSet = -1;
37165 
37166     if(which_set >= num_difficulties)
37167     {
37168         return;
37169     }
37170     // borShutdown(1, "Illegal set chosen: index %i (there are only %i sets)!", which_set, num_difficulties);
37171 
37172     allow_secret_chars = set->ifcomplete;
37173     PLAYER_LIVES = set->lives;
37174     musicoverlap = set->musicoverlap;
37175     fade = set->custfade;
37176     CONTINUES = set->credits;
37177     magic_type = set->typemp;
37178     if(PLAYER_LIVES == 0)
37179     {
37180         PLAYER_LIVES = 3; // default
37181     }
37182     if(CONTINUES == 0)
37183     {
37184         CONTINUES = 5; // default
37185     }
37186     if(fade == 0)
37187     {
37188         fade = 24; // default
37189     }
37190     sameplayer = set->nosame;
37191 
37192     if(useSavedGame == 1 && save->flag)
37193     {
37194         memset(player, 0, sizeof(*player) * 4);
37195         if(!loadScriptFile())
37196         {
37197             printf("Warning, failed to load script save!\n");
37198         }
37199         current_level = save->level;
37200         current_stage = save->stage;
37201         if(save->flag == 2) // don't check 1 or 0 becuase if we use saved game the flag must be >0
37202         {
37203             for(i = 0; i < set->maxplayers; i++)
37204             {
37205                 player[i].lives = save->pLives[i];
37206                 player[i].credits = save->pCredits[i];
37207                 player[i].score = save->pScores[i];
37208                 player[i].colourmap = save->pColourmap[i];
37209                 player[i].weapnum = save->pWeapnum[i];
37210                 player[i].spawnhealth = save->pSpawnhealth[i];
37211                 player[i].spawnmp = save->pSpawnmp[i];
37212                 strncpy(player[i].name, save->pName[i], MAX_NAME_LEN);
37213             }
37214             credits = save->credits;
37215         }
37216         load_playable_list(save->allowSelectArgs); //TODO: change sav format to support dynamic allowselect list.
37217         //reset_playable_list(1); // add this because there's no select screen, temporary solution
37218     }
37219 
37220     nosave = 1;
37221 
37222     // fix save an inexistent level
37223     if ( current_level >= set->numlevels )
37224     {
37225         current_level = set->numlevels-1;
37226     }
37227 
37228     le = set->levelorder + current_level;
37229     set->noselect = le->noselect;
37230     if (save->selectSkipSelect[0]) le = load_skipselect(save->selectSkipSelect, set); // LOAD skipselect
37231     for(i = 0; i < MAX_PLAYERS; i++)
37232     {
37233         if(le->skipselect[i])
37234         {
37235             strcpy(skipselect[i], le->skipselect[i]);
37236         }
37237         else
37238         {
37239             skipselect[i][0] = 0;
37240         }
37241     }
37242 
37243     if((useSavedGame == 1 && save->flag == 2) || useSavedGame == 2 || selectplayer(players, NULL, useSavedGame)) // if save flag is 2 don't select player
37244     {
37245         while(current_level < set->numlevels)
37246         {
37247             if(branch_name[0])  // branch checking
37248             {
37249                 //current_stage = 1; //jump, jump... perhaps we don't need to reset it, modders should take care of it.
37250                 for(i = 0; i < set->numlevels; i++)
37251                 {
37252                     if(set->levelorder[i].branchname && stricmp(set->levelorder[i].branchname, branch_name) == 0)
37253                     {
37254                         current_level = i;
37255                         break;
37256                     }
37257                     //if(levelorder[which_set][i]->gonext==1) ++current_stage; OX. Commented this line out. Seems to be cause of inacurate stage # complete message.
37258                 }
37259                 branch_name[0] = 0;// clear up so we won't stuck here
37260             }
37261             le = set->levelorder + current_level;
37262             PLAYER_MIN_Z = le->z_coords[0];
37263             PLAYER_MAX_Z = le->z_coords[1];
37264             BGHEIGHT = le->z_coords[2];
37265 
37266             if(le->type == LE_TYPE_CUT_SCENE)
37267             {
37268                 playscene(le->filename);
37269             }
37270             else if(le->type == LE_TYPE_SELECT_SCREEN)
37271             {
37272                 memset(save->selectSkipSelect,0,sizeof(save->selectSkipSelect)); // RESET skipselect
37273                 set->noselect = 0;
37274                 for(i = 0; i < set->maxplayers; i++) // reset skipselect
37275                 {
37276                     if(le->skipselect[i])
37277                     {
37278                         skipselect[i][0] = 0;
37279                     }
37280                 }
37281                 for(i = 0; i < set->maxplayers ; i++)
37282                 {
37283                     players[i] = (player[i].lives > 0);
37284                 }
37285                 if(selectplayer(players, le->filename, useSavedGame) == 0)
37286                 {
37287                     break;
37288                 }
37289             }
37290             else if(le->type == LE_TYPE_SKIP_SELECT)
37291             {
37292                 set->noselect = le->noselect;
37293                 for(i = 0; i < MAX_PLAYERS; i++)
37294                 {
37295                     if(le->skipselect[i])
37296                     {
37297                         strcpy(skipselect[i], le->skipselect[i]);
37298                     }
37299                     else
37300                     {
37301                         skipselect[i][0] = 0;
37302                     }
37303                 }
37304                 save_skipselect(save->selectSkipSelect, skipselect); // TO SAVE FILE
37305                 selectplayer(players, NULL, useSavedGame); // re-select a player
37306             }
37307             else if(!playlevel(le->filename))
37308             {
37309                 int all_p_lives_zero = 0;
37310                 for(i = 0; i < MAX_PLAYERS; i++)
37311                 {
37312                     if (player[i].lives <= 0) ++all_p_lives_zero;
37313                 }
37314                 all_p_lives_zero = (all_p_lives_zero >= MAX_PLAYERS) ? 1 : 0;
37315 
37316                 //if( (player[0].lives <= 0 && player[1].lives <= 0 && player[2].lives <= 0 && player[3].lives <= 0) )
37317                 if(all_p_lives_zero)
37318                 {
37319                     if( (!set->noshowgameover && !(goto_mainmenu_flag&2)) )
37320                     {
37321                         gameover();
37322                     }
37323                     if(!set->noshowhof && !(goto_mainmenu_flag&4))
37324                     {
37325                         hallfame(1);
37326                     }
37327                     for(i = 0; i < set->maxplayers; i++)
37328                     {
37329                         player[i].hasplayed = 0;
37330                         player[i].weapnum = 0;
37331                     }
37332                 }
37333                 break;
37334             }
37335             if(le->gonext == 1)
37336             {
37337                 if (!set->noshowcomplete) showcomplete(current_stage);
37338                 for(i = 0; i < set->maxplayers; i++)
37339                 {
37340                     player[i].spawnhealth = 0;
37341                     player[i].spawnmp = 0;
37342                 }
37343                 ++current_stage;
37344                 save->stage = current_stage;
37345             }
37346             current_level++;
37347             le = set->levelorder + current_level;
37348             save->level = current_level;
37349             //2007-2-24, gonext = 2, end game
37350             if((le - 1)->gonext == 2)
37351             {
37352                 current_level = set->numlevels;
37353             }
37354             if(useSave)
37355             {
37356                 goto endgame;    //quick exit without saving, for script load game logic
37357             }
37358         }//while
37359 
37360         if(current_level >= set->numlevels)
37361         {
37362             bonus += save->times_completed++;
37363             saveGameFile();
37364             if(!nofadeout)
37365             {
37366                 fade_out(0, 0);
37367             }
37368             if(!set->noshowhof)
37369             {
37370                 hallfame(1);
37371             }
37372         }
37373     }
37374 
37375 endgame:
37376     // clear global script variant list
37377     branch_name[0] = 0;
37378     sound_close_music();
37379 }
37380 
menu_difficulty()37381 int menu_difficulty()
37382 {
37383     int quit = 0;
37384     int selector = 0;
37385     int maxdisplay = 5;
37386     int i, j, t;
37387     //float slider = 0;
37388     int barx, bary, barw, barh;
37389     s_drawmethod drawmethod = plainmethod;
37390     drawmethod.alpha = BLEND_MODE_ALPHA;
37391 
37392     barx = videomodes.hRes / 5;
37393     bary = _liney(0, 0) - 2;
37394     barw = videomodes.hRes * 3 / 5;
37395     barh = 5 * (fontheight(0) + 1) + 4;
37396     newgameMenu = 1;
37397     bothnewkeys = 0;
37398 
37399     loadGameFile();
37400 
37401     while(!quit)
37402     {
37403         if(num_difficulties > 1)
37404         {
37405             _menutextm(2, -2, 0, Tr("Game Mode"));
37406             t = (selector - (selector == num_difficulties)) / maxdisplay * maxdisplay;
37407             for(j = 0, i = t; i < maxdisplay + t && i < num_difficulties; j++, i++)
37408             {
37409                 if(j < maxdisplay)
37410                 {
37411                     if(bonus >= levelsets[i].ifcomplete)
37412                     {
37413                         _menutextm((selector == i), j, 0, "%s", levelsets[i].name);
37414                     }
37415                     else
37416                     {
37417                         if(levelsets[i].ifcomplete > 1)
37418                         {
37419                             _menutextm((selector == i), j, 0, Tr("%s - Finish Game %i Times To UnLock"), levelsets[i].name, levelsets[i].ifcomplete);
37420                         }
37421                         else
37422                         {
37423                             _menutextm((selector == i), j, 0, Tr("%s - Finish Game To UnLock"), levelsets[i].name);
37424                         }
37425                     }
37426                 }
37427                 else
37428                 {
37429                     break;
37430                 }
37431             }
37432             _menutextm((selector == i), 6, 0, Tr("Back"));
37433 
37434             //draw the scroll bar
37435             if(num_difficulties > maxdisplay)
37436             {
37437                 spriteq_add_box(barx,  bary,        barw,     barh,   0, color_black, &drawmethod); //outerbox
37438                 spriteq_add_line(barx, bary,  barx + 8, bary, 1, color_white, NULL);
37439                 spriteq_add_line(barx, bary, barx, bary + barh, 1, color_white, NULL);
37440                 spriteq_add_line(barx + 8, bary, barx + 8, bary + barh,  1, color_white, NULL);
37441                 spriteq_add_line(barx, bary + barh, barx + 8, bary + barh,  1, color_white, NULL);
37442                 spriteq_add_box(barx + 1,  bary + selector * (barh - 3) / num_difficulties, 7,             3,            2, color_white, NULL); //slider
37443             }
37444         }
37445 
37446         update(0, 0);
37447 
37448         if(num_difficulties == 1) // OX. Mods with only one set will auto load that difficulty.
37449         {
37450             if(selector == num_difficulties)
37451             {
37452                 quit = 1;
37453             }
37454             else if(bonus >= levelsets[selector].ifcomplete)
37455             {
37456                 saveslot = selector;
37457                 strncpy(savelevel[saveslot].dName, levelsets[saveslot].name, MAX_NAME_LEN - 1);
37458                 newgameMenu = 0;
37459                 return saveslot;
37460             }
37461         }
37462 
37463         if(bothnewkeys & FLAG_ESC)
37464         {
37465             quit = 1;
37466         }
37467         if(bothnewkeys & FLAG_MOVEUP)
37468         {
37469             --selector;
37470             if(SAMPLE_BEEP >= 0)
37471             {
37472                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37473             }
37474         }
37475         if(bothnewkeys & FLAG_MOVEDOWN)
37476         {
37477             ++selector;
37478             if(SAMPLE_BEEP >= 0)
37479             {
37480                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37481             }
37482         }
37483         if(selector < 0)
37484         {
37485             selector = num_difficulties;
37486         }
37487         if(selector > num_difficulties)
37488         {
37489             selector = 0;
37490         }
37491         //if(selector<num_difficulties) slider = selector * 4.5;
37492 
37493         if(bothnewkeys & FLAG_ANYBUTTON)
37494         {
37495 
37496             if(SAMPLE_BEEP2 >= 0)
37497             {
37498                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
37499             }
37500 
37501             if(selector == num_difficulties)
37502             {
37503                 quit = 1;
37504             }
37505             else if(bonus >= levelsets[selector].ifcomplete)
37506             {
37507                 saveslot = selector;
37508                 strncpy(savelevel[saveslot].dName, levelsets[saveslot].name, MAX_NAME_LEN - 1);
37509                 newgameMenu = 0;
37510                 return saveslot;
37511             }
37512         }
37513     }
37514     bothnewkeys = 0;
37515     newgameMenu = 0;
37516     return -1;
37517 }
37518 
load_saved_game()37519 int load_saved_game()
37520 {
37521     int quit = 0;
37522     int selector = 0;
37523     int savedStatus = 0;
37524     char name[MAX_BUFFER_LEN] = {""};
37525     int col1 = -8, col2 = 6;
37526 
37527     loadgameMenu = 1;
37528     bothnewkeys = 0;
37529 
37530     if((savedStatus = loadGameFile()))
37531     {
37532         getPakName(name, 0);
37533     }
37534     for(saveslot = 0; saveslot < num_difficulties; saveslot++) if(savelevel[saveslot].flag && savelevel[saveslot].level)
37535         {
37536             break;
37537         }
37538 
37539     while(!quit)
37540     {
37541         if(saveslot >= num_difficulties) // not found
37542         {
37543             _menutextm(2, -4, 0, Tr("Load Game"));
37544             _menutext(0, col1, -2, Tr("Saved File:"));
37545             _menutext(0, col2, -2, Tr("Not Found!"));
37546             _menutextm(1, 6, 0, Tr("Back"));
37547 
37548             selector = 2;
37549         }
37550         else
37551         {
37552             _menutextm(2, -4, 0, Tr("Load Game"));
37553             _menutext(0, col1, -2, Tr("Saved File:"));
37554             if(savedStatus)
37555             {
37556                 _menutext(0, col2, -2, "%s", name);
37557             }
37558             else
37559             {
37560                 _menutext(0, col2, -2, Tr("Not Found!"));
37561             }
37562 
37563             if(savedStatus)
37564             {
37565                 _menutext((selector == 0), col1, -1, Tr("Mode:"));
37566                 _menutext((selector == 0), col2, -1, "%s", savelevel[saveslot].dName);
37567                 _menutext(0, col1, 0, Tr("Stage:"));
37568                 _menutext(0, col2, 0, "%d", savelevel[saveslot].stage);
37569                 _menutext(0, col1, 1, Tr("Level:"));
37570                 _menutext(0, col2, 1, "%d", savelevel[saveslot].level);
37571                 _menutext(0, col1, 2, Tr("Credits:"));
37572 
37573                 if(noshare){
37574                     _menutext(0, col2, 2, "%d/%d/%d/%d",
37575                                   savelevel[saveslot].pCredits[0],
37576                                   savelevel[saveslot].pCredits[1], savelevel[saveslot].pCredits[2],
37577                                   savelevel[saveslot].pCredits[3]);
37578                 } else {
37579                     _menutext(0, col2, 2, "%d", savelevel[saveslot].credits);
37580                 }
37581 
37582                 _menutext(0, col1, 3, Tr("Player Lives:"));
37583                 _menutext(0, col2, 3, "%d/%d/%d/%d",
37584                           savelevel[saveslot].pLives[0],
37585                           savelevel[saveslot].pLives[1], savelevel[saveslot].pLives[2],
37586                           savelevel[saveslot].pLives[3]);
37587             }
37588             _menutextm((selector == 1), 6, 0, Tr("Back"));
37589         }
37590         update(0, 0);
37591 
37592         if(bothnewkeys & FLAG_ESC)
37593         {
37594             quit = 1;
37595         }
37596         if(selector == 0 && (bothnewkeys & FLAG_MOVELEFT))
37597         {
37598             while(1)
37599             {
37600                 --saveslot;
37601                 if(saveslot < 0)
37602                 {
37603                     saveslot = num_difficulties - 1;
37604                 }
37605                 if(savelevel[saveslot].flag && savelevel[saveslot].level)
37606                 {
37607                     break;
37608                 }
37609             }
37610             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37611         }
37612         if(selector == 0 && (bothnewkeys & FLAG_MOVERIGHT))
37613         {
37614             while(1)
37615             {
37616                 ++saveslot;
37617                 if(saveslot > num_difficulties - 1)
37618                 {
37619                     saveslot = 0;
37620                 }
37621                 if(savelevel[saveslot].flag && savelevel[saveslot].level)
37622                 {
37623                     break;
37624                 }
37625             }
37626             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37627         }
37628         if(bothnewkeys & FLAG_MOVEUP)
37629         {
37630             --selector;
37631             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37632         }
37633         if(bothnewkeys & FLAG_MOVEDOWN)
37634         {
37635             ++selector;
37636             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37637         }
37638         if(savedStatus)
37639         {
37640             if(selector < 0)
37641             {
37642                 selector = 1;
37643             }
37644             if(selector > 1)
37645             {
37646                 selector = 0;
37647             }
37648         }
37649         else
37650         {
37651             selector = 1;
37652         }
37653 
37654         if((bothnewkeys & FLAG_ANYBUTTON))
37655         {
37656             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
37657             switch(selector)
37658             {
37659             case 0:
37660                 return saveslot;
37661                 break;
37662             case 1:
37663                 quit = 1;
37664                 break;
37665             }
37666         }
37667     }
37668     bothnewkeys = 0;
37669     loadgameMenu = 0;
37670     return -1;
37671 }
37672 
choose_mode(int * players)37673 int choose_mode(int *players)
37674 {
37675     int quit = 0;
37676     int relback = 0;
37677     int selector = 0;
37678     int status = 0;
37679 
37680     startgameMenu = 1;
37681     bothnewkeys = 0;
37682 
37683     while(!quit)
37684     {
37685         _menutextm(2, 1, 0, Tr("Choose Mode"));
37686         _menutextm((selector == 0), 3, 0, Tr("New Game"));
37687         _menutextm((selector == 1), 4, 0, Tr("Load Game"));
37688         _menutextm((selector == 2), 6, 0, Tr("Back"));
37689 
37690         update(0, 0);
37691 
37692         if(bothnewkeys & FLAG_ESC)
37693         {
37694             quit = 1;
37695         }
37696         if(bothnewkeys & FLAG_MOVEUP)
37697         {
37698             --selector;
37699             if(SAMPLE_BEEP >= 0)
37700             {
37701                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37702             }
37703         }
37704         if(bothnewkeys & FLAG_MOVEDOWN)
37705         {
37706             ++selector;
37707             if(SAMPLE_BEEP >= 0)
37708             {
37709                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
37710             }
37711         }
37712         if(selector < 0)
37713         {
37714             selector = 2;
37715         }
37716         if(selector > 2)
37717         {
37718             selector = 0;
37719         }
37720 
37721         if(bothnewkeys & FLAG_ANYBUTTON)
37722         {
37723             if(SAMPLE_BEEP2 >= 0)
37724             {
37725                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
37726             }
37727             switch(selector)
37728             {
37729             case 0:
37730                 status = menu_difficulty();
37731                 if(status != -1)
37732                 {
37733                     playgame(players, status, 0);
37734                     relback = 1;
37735                     quit = 1;
37736                 }
37737                 break;
37738             case 1:
37739                 status = load_saved_game();
37740                 if(status != -1)
37741                 {
37742                     playgame(players, status, 1);
37743                     relback = 1;
37744                     quit = 1;
37745                 }
37746                 break;
37747             default:
37748                 quit = 1;
37749                 break;
37750             }
37751         }
37752     }
37753     bothnewkeys = 0;
37754     startgameMenu = 0;
37755     return relback;
37756 }
37757 
term_videomodes()37758 void term_videomodes()
37759 {
37760     videomodes.hRes = 0;
37761     videomodes.vRes = 0;
37762     video_set_mode(videomodes);
37763     if(custScenes != NULL)
37764     {
37765         free(custScenes);
37766     }
37767     custScenes = NULL;
37768     if(custBkgrds != NULL)
37769     {
37770         free(custBkgrds);
37771     }
37772     custBkgrds = NULL;
37773     if(custLevels != NULL)
37774     {
37775         free(custLevels);
37776     }
37777     custLevels = NULL;
37778     if(custModels != NULL)
37779     {
37780         free(custModels);
37781     }
37782     custModels = NULL;
37783 }
37784 
37785 // Load Video Mode from file
init_videomodes(int log)37786 void init_videomodes(int log)
37787 {
37788     char *filename = "data/video.txt";
37789     int tmp;
37790     ptrdiff_t pos, len;
37791     size_t size;
37792     char *buf = NULL;
37793     char *command = NULL;
37794     char *value = NULL, *value2;
37795     ArgList arglist;
37796     char argbuf[MAX_ARG_LEN + 1] = "";
37797 
37798     //
37799 
37800     if(log)
37801     {
37802         printf("Initializing video............\n");
37803     }
37804 
37805     // Use an alternative video.txt if there is one.  Some of these are long filenames; create your PAKs with borpak and you'll be fine.
37806 #define tryfile(X) if((tmp=openpackfile(X,packfile))!=-1) { closepackfile(tmp); filename=X; goto readfile; }
37807 #if WIN || LINUX
37808     tryfile("data/videopc.txt");
37809 #elif WII
37810     tryfile("data/videowii.txt");
37811     if(CONF_GetAspectRatio() == CONF_ASPECT_16_9)
37812     {
37813         tryfile("data/video169.txt")
37814     }
37815     else
37816     {
37817         tryfile("data/video43.txt");
37818     }
37819 #elif PSP
37820     tryfile("data/videopsp.txt");
37821     tryfile("data/video169.txt");
37822 #elif DC
37823     tryfile("data/videodc.txt");
37824     tryfile("data/video43.txt");
37825 #elif WIZ
37826     tryfile("data/videowiz.txt");
37827     tryfile("data/video169.txt");
37828 #elif GP2X
37829     tryfile("data/videogp2x.txt");
37830     tryfile("data/video43.txt");
37831 #elif OPENDINGUX
37832     tryfile("data/videoopendingux.txt");
37833     tryfile("data/video43.txt");
37834 #elif SYMBIAN
37835     tryfile("data/videosymbian.txt");
37836 #elif VITA
37837     tryfile("data/videovita.txt");
37838     tryfile("data/video169.txt");
37839 #endif
37840 #undef tryfile
37841 
37842 readfile:
37843     // Read file
37844     if(buffer_pakfile(filename, &buf, &size) != 1)
37845     {
37846         videoMode = 0;
37847         printf("'%s' not found.\n", filename);
37848         goto VIDEOMODES;
37849     }
37850 
37851     printf("Reading video settings from '%s'.\n", filename);
37852 
37853     // Now interpret the contents of buf line by line
37854     pos = 0;
37855     while(pos < size)
37856     {
37857         ParseArgs(&arglist, buf + pos, argbuf);
37858         command = GET_ARG(0);
37859 
37860         if(command && command[0])
37861         {
37862             if(stricmp(command, "video") == 0)
37863             {
37864                 value = GET_ARG(1);
37865                 if((value2 = strchr(value, 'x')))
37866                 {
37867                     videomodes.hRes = atoi(value);
37868                     videomodes.vRes = atoi(value2 + 1);
37869                     videoMode = 255;
37870                 }
37871                 else
37872                 {
37873                     videoMode = GET_INT_ARG(1);
37874                 }
37875             }
37876             else if(stricmp(command, "scenes") == 0)
37877             {
37878                 len = strlen(GET_ARG(1));
37879                 custScenes = malloc(len + 1);
37880                 strcpy(custScenes, GET_ARG(1));
37881                 custScenes[len] = 0;
37882             }
37883             else if(stricmp(command, "backgrounds") == 0)
37884             {
37885                 len = strlen(GET_ARG(1));
37886                 custBkgrds = malloc(len + 1);
37887                 strcpy(custBkgrds, GET_ARG(1));
37888                 custBkgrds[len] = 0;
37889             }
37890             else if(stricmp(command, "levels") == 0)
37891             {
37892                 len = strlen(GET_ARG(1));
37893                 custLevels = malloc(len + 1);
37894                 strcpy(custLevels, GET_ARG(1));
37895                 custLevels[len] = 0;
37896             }
37897             else if(stricmp(command, "models") == 0)
37898             {
37899                 len = strlen(GET_ARG(1));
37900                 custModels = malloc(len + 1);
37901                 strcpy(custModels, GET_ARG(1));
37902                 custModels[len] = 0;
37903             }
37904             else if(stricmp(command, "colourdepth") == 0)
37905             {
37906                 printf("\nColordepth is depreciated. All modules are displayed with a 32bit color screen.\n\n");
37907             }
37908             else if(stricmp(command, "forcemode") == 0) {}
37909             else if(command && command[0])
37910             {
37911                 printf("Command '%s' not understood in file '%s'!\n", command, filename);
37912             }
37913         }
37914         // Go to next line
37915         pos += getNewLineStart(buf + pos);
37916     }
37917 
37918     if(buf != NULL)
37919     {
37920         free(buf);
37921         buf = NULL;
37922     }
37923 
37924 #if OPENDINGUX || GP2X
37925     videoMode = 0;
37926 #endif
37927 
37928 #if SYMBIAN
37929     if(videoMode != 0 && videoMode != 2)
37930     {
37931         videoMode = 0;
37932     }
37933 #endif
37934 
37935 VIDEOMODES:
37936     videomodes.mode    = videoMode;
37937     videomodes.filter  = savedata.swfilter;
37938     switch (videoMode)
37939     {
37940         // 320x240 - All Platforms
37941     case 0:
37942         videomodes.hRes    = 320;
37943         videomodes.vRes    = 240;
37944         videomodes.hScale  = 1;
37945         videomodes.vScale  = 1;
37946         videomodes.hShift  = 0;
37947         videomodes.vShift  = 0;
37948         videomodes.dOffset = 231;
37949         PLAYER_MIN_Z       = 160;
37950         PLAYER_MAX_Z       = 232;
37951         BGHEIGHT           = 160;
37952         break;
37953 
37954         // 480x272 - All Platforms
37955     case 1:
37956         videomodes.hRes    = 480;
37957         videomodes.vRes    = 272;
37958         videomodes.hScale  = (float)1.5;
37959         videomodes.vScale  = (float)1.13;
37960         videomodes.hShift  = 80;
37961         videomodes.vShift  = 20;
37962         videomodes.dOffset = 263;
37963         PLAYER_MIN_Z       = 182;
37964         PLAYER_MAX_Z       = 264;
37965         BGHEIGHT           = 182;
37966         break;
37967 
37968         // 640x480 - PC, Dreamcast, Wii
37969     case 2:
37970         videomodes.hRes    = 640;
37971         videomodes.vRes    = 480;
37972         videomodes.hScale  = 2;
37973         videomodes.vScale  = 2;
37974         videomodes.hShift  = 160;
37975         videomodes.vShift  = 35;
37976         videomodes.dOffset = 464;
37977         PLAYER_MIN_Z       = 321;
37978         PLAYER_MAX_Z       = 465;
37979         BGHEIGHT           = 321;
37980         break;
37981 
37982         // 720x480 - PC, Wii
37983     case 3:
37984         videomodes.hRes    = 720;
37985         videomodes.vRes    = 480;
37986         videomodes.hScale  = 2.25;
37987         videomodes.vScale  = 2;
37988         videomodes.hShift  = 200;
37989         videomodes.vShift  = 35;
37990         videomodes.dOffset = 464;
37991         PLAYER_MIN_Z       = 321;
37992         PLAYER_MAX_Z       = 465;
37993         BGHEIGHT           = 321;
37994         break;
37995 
37996         // 800x480 - PC, Wii, Pandora
37997     case 4:
37998         videomodes.hRes    = 800;
37999         videomodes.vRes    = 480;
38000         videomodes.hScale  = 2.5;
38001         videomodes.vScale  = 2;
38002         videomodes.hShift  = 240;
38003         videomodes.vShift  = 35;
38004         videomodes.dOffset = 464;
38005         PLAYER_MIN_Z       = 321;
38006         PLAYER_MAX_Z       = 465;
38007         BGHEIGHT           = 321;
38008         break;
38009 
38010         // 800x600 - PC, Dreamcast, Wii
38011     case 5:
38012         videomodes.hRes    = 800;
38013         videomodes.vRes    = 600;
38014         videomodes.hScale  = 2.5;
38015         videomodes.vScale  = 2.5;
38016         videomodes.hShift  = 240;
38017         videomodes.vShift  = 44;
38018         videomodes.dOffset = 580;
38019         PLAYER_MIN_Z       = 401;
38020         PLAYER_MAX_Z       = 582;
38021         BGHEIGHT           = 401;
38022         break;
38023 
38024         // 960x540 - PC, Wii
38025     case 6:
38026         videomodes.hRes    = 960;
38027         videomodes.vRes    = 540;
38028         videomodes.hScale  = 3;
38029         videomodes.vScale  = 2.25;
38030         videomodes.hShift  = 320;
38031         videomodes.vShift  = 40;
38032         videomodes.dOffset = 522;
38033         PLAYER_MIN_Z       = 362;
38034         PLAYER_MAX_Z       = 524;
38035         BGHEIGHT           = 362;
38036         break;
38037 
38038     case 255:
38039         videomodes.dOffset = videomodes.vRes * 0.9625;
38040         printf("\nUsing debug video mode: %d x %d\n", videomodes.hRes, videomodes.vRes);
38041         break;
38042     default:
38043         borShutdown(1, "Invalid video mode: %d in 'data/video.txt', supported modes:\n"
38044                  "0 - 320x240\n"
38045                  "1 - 480x272\n"
38046                  "2 - 640x480\n"
38047                  "3 - 720x480\n"
38048                  "4 - 800x480\n"
38049                  "5 - 800x600\n"
38050                  "6 - 960x540\n\n", videoMode);
38051         break;
38052     }
38053 
38054 #if SDL || WII
38055     video_stretch(savedata.stretch);
38056 #endif
38057 
38058     if((vscreen = allocscreen(videomodes.hRes, videomodes.vRes, PIXEL_32)) == NULL)
38059     {
38060         borShutdown(1, "Not enough memory!\n");
38061     }
38062     videomodes.pixel = pixelbytes[(int)vscreen->pixelformat];
38063     //video_set_mode(videomodes);
38064     clearscreen(vscreen);
38065 
38066     if(log)
38067     {
38068         printf("Initialized video.............\t%dx%d (Mode: %d)\n\n", videomodes.hRes, videomodes.vRes, videoMode);
38069     }
38070 }
38071 
38072 
38073 
38074 // ----------------------------------------------------------------------------
38075 
38076 
38077 // Set key or button safely (with switching)
safe_set(int * arr,int index,int newkey,int oldkey)38078 void safe_set(int *arr, int index, int newkey, int oldkey)
38079 {
38080     int i;
38081     for(i = 0; i < 12; i++)
38082     {
38083         if(arr[i] == newkey)
38084         {
38085             arr[i] = oldkey;
38086         }
38087     }
38088     arr[index] = newkey;
38089 }
38090 
keyboard_setup(int player)38091 void keyboard_setup(int player)
38092 {
38093     const int btnnum = MAX_BTN_NUM;
38094     int quit = 0, sdid = 0,
38095         selector = 0,
38096         setting = -1,
38097         i, k, ok = 0,
38098               disabledkey[MAX_BTN_NUM] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
38099                                 col1 = -8, col2 = 6;
38100     ptrdiff_t voffset, pos;
38101     char *buf,
38102          *command,
38103          *filename = "data/menu.txt",
38104          buttonnames[btnnum][32];
38105     size_t size;
38106     ArgList arglist;
38107     char argbuf[MAX_ARG_LEN + 1] = "";
38108     #if SDL || WII
38109     int OPTIONS_NUM = btnnum + 3;
38110     #else
38111     int OPTIONS_NUM = btnnum + 2;
38112     #endif
38113 
38114     printf("Loading control settings.......\t");
38115 
38116     strcpy(buttonnames[SDID_MOVEUP], "Move Up");
38117     strcpy(buttonnames[SDID_MOVEDOWN], "Move Down");
38118     strcpy(buttonnames[SDID_MOVELEFT], "Move Left");
38119     strcpy(buttonnames[SDID_MOVERIGHT], "Move Right");
38120     strcpy(buttonnames[SDID_ATTACK], "Attack 1");
38121     strcpy(buttonnames[SDID_ATTACK2], "Attack 2");
38122     strcpy(buttonnames[SDID_ATTACK3], "Attack 3");
38123     strcpy(buttonnames[SDID_ATTACK4], "Attack 4");
38124     strcpy(buttonnames[SDID_JUMP], "Jump");
38125     strcpy(buttonnames[SDID_SPECIAL], "Special");
38126     strcpy(buttonnames[SDID_START], "Start");
38127     strcpy(buttonnames[SDID_SCREENSHOT], "Screenshot");
38128     strcpy(buttonnames[SDID_ESC], "Exit");
38129 
38130     savesettings();
38131     bothnewkeys = 0;
38132 
38133     // Read file
38134     if(buffer_pakfile(filename, &buf, &size))
38135     {
38136         // Now interpret the contents of buf line by line
38137         pos = 0;
38138         while(pos < size)
38139         {
38140             ParseArgs(&arglist, buf + pos, argbuf);
38141             command = GET_ARG(0);
38142             if(command[0])
38143             {
38144                 if(stricmp(command, "disablekey") == 0)
38145                 {
38146                     sdid = translate_SDID(GET_ARG(1));
38147                     if(sdid >= 0)
38148                     {
38149                         disabledkey[sdid] = 1;
38150                     }
38151                 }
38152                 else if(stricmp(command, "renamekey") == 0)
38153                 {
38154                     sdid = translate_SDID(GET_ARG(1));
38155                     if(sdid >= 0)
38156                     {
38157                         strcpy(buttonnames[sdid], GET_ARG(2));
38158                     }
38159                 }
38160 
38161             }
38162             // Go to next line
38163             pos += getNewLineStart(buf + pos);
38164         }
38165         if(buf != NULL)
38166         {
38167             free(buf);
38168             buf = NULL;
38169         }
38170     }
38171 
38172     while(disabledkey[selector]) if(++selector > btnnum - 1) break;
38173 
38174     while(!quit)
38175     {
38176         int deviceID = playercontrolpointers[player]->deviceID;
38177         int *mapping = control_getmappings(deviceID);
38178 
38179         voffset = -6;
38180         _menutextm(2, -8, 0, Tr("Player %i"), player + 1);
38181         for(i = 0; i < btnnum; i++)
38182         {
38183             if(!disabledkey[i])
38184             {
38185                 _menutext((selector == i), col1, voffset, "%s", buttonnames[i]);
38186                 _menutext((selector == i), col2, voffset, "%s", i == setting ? "..." : control_getkeyname(deviceID, mapping[i]));
38187                 voffset++;
38188             }
38189         }
38190         #if SDL || WII
38191         ++voffset;
38192         if(savedata.joyrumble[player])
38193         {
38194             _menutext((selector == OPTIONS_NUM - 3), col1, voffset++, Tr("Rumble Enabled"));
38195         }
38196         else
38197         {
38198             _menutext((selector == OPTIONS_NUM - 3), col1, voffset++, Tr("Rumble Disabled"));
38199         }
38200         #endif
38201 
38202         _menutextm((selector == OPTIONS_NUM - 2), ++voffset, 0, Tr("OK"));
38203         _menutextm((selector == OPTIONS_NUM - 1), ++voffset, 0, Tr("Cancel"));
38204         _menutextm((selector == OPTIONS_NUM), ++voffset, 0, Tr("Default"));
38205         update((level != NULL), 0);
38206 
38207         if(setting > -1)
38208         {
38209             k = control_getremappedkey();
38210             if (k >= 0)
38211             {
38212                 safe_set(mapping, setting, k, ok);
38213                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38214 
38215                 // Prevent the newly configured button from counting as "pressed" and starting config again
38216                 playercontrolpointers[player]->keyflags |= (1 << setting);
38217 
38218                 setting = -1;
38219                 control_remapdevice(-1);
38220             }
38221             else if (bothnewkeys & FLAG_ESC)
38222             {
38223                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 50);
38224                 setting = -1;
38225             }
38226         }
38227         else
38228         {
38229             if(bothnewkeys & FLAG_ESC)
38230             {
38231                 quit = 1;
38232             }
38233             if(bothnewkeys & FLAG_MOVEUP)
38234             {
38235                 do
38236                 {
38237                     if(--selector < 0)
38238                     {
38239                         break;
38240                     }
38241                 }
38242                 while(selector < btnnum && disabledkey[selector]);
38243                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38244             }
38245             if(bothnewkeys & FLAG_MOVEDOWN)
38246             {
38247                 do
38248                 {
38249                     if(++selector > btnnum - 1) break;
38250                 }
38251                 while(disabledkey[selector]);
38252                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38253             }
38254             if(selector < 0)
38255             {
38256                 selector = OPTIONS_NUM;
38257             }
38258             if(selector > OPTIONS_NUM)
38259             {
38260                 selector = 0;
38261                 while(disabledkey[selector]) if(++selector > btnnum - 1) break;
38262             }
38263 
38264 #if SDL || WII
38265             if (selector == OPTIONS_NUM - 3 && (bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)))
38266             {
38267                 // TODO: make rumble enable/disable a property of device, not player
38268                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38269                 savedata.joyrumble[player] = !savedata.joyrumble[player];
38270             }
38271             else
38272 #endif
38273             if (bothnewkeys & FLAG_ANYBUTTON)
38274             {
38275                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38276 
38277                 if(selector == OPTIONS_NUM - 2) // OK
38278                 {
38279                     quit = 2;
38280                 }
38281                 else if(selector == OPTIONS_NUM - 1) // cancel
38282                 {
38283                     quit = 1;
38284                 }
38285                 else if(selector == OPTIONS_NUM) // default
38286                 {
38287                     control_resetmappings(deviceID);
38288                     savedata.joyrumble[player] = 0;
38289                 }
38290                 else
38291                 {
38292                     setting = selector;
38293                     ok = mapping[setting];
38294                     control_remapdevice(deviceID);
38295                 }
38296             }
38297         }
38298     }
38299 
38300     if(quit == 2)
38301     {
38302         apply_controls();
38303         savesettings();
38304     }
38305     else
38306     {
38307         loadsettings();
38308     }
38309 
38310     update(0, 0);
38311     bothnewkeys = 0;
38312     printf("Done!\n");
38313 }
38314 
38315 // Set device safely (with switching)
safe_set_device(int player,int newdevice,int olddevice)38316 static void safe_set_device(int player, int newdevice, int olddevice)
38317 {
38318     int i;
38319     for (i = 0; i < levelsets[current_set].maxplayers; i++)
38320     {
38321         if (playercontrolpointers[i]->deviceID == newdevice)
38322         {
38323             playercontrolpointers[i]->deviceID = olddevice;
38324         }
38325     }
38326     playercontrolpointers[player]->deviceID = newdevice;
38327 }
38328 
menu_options_input()38329 void menu_options_input()
38330 {
38331     int quit = 0;
38332     int selector = 0;
38333     const int col1 = -7;
38334     const int col2 = 1;
38335     const int max_players = levelsets[current_set].maxplayers;
38336     const int base = -4 + (4 - max_players);
38337     int active_devices = 0;
38338     int selected_device = playercontrolpointers[0]->deviceID;
38339     #if ANDROID
38340     const int OPTIONS_NUM = 9;
38341     #else
38342     const int OPTIONS_NUM = 8;
38343     #endif
38344 
38345     controloptionsMenu = 1;
38346     bothnewkeys = 0;
38347 
38348     while (!quit)
38349     {
38350         _menutextm(2, base - 2, 0, Tr("Control Options"));
38351 
38352         for (int i = 0; i < max_players; i++)
38353         {
38354             _menutext((selector == i), col1, base + i + (selector < i), Tr("Player %i"), i+1);
38355             _menutext((selector == i), col2, base + i + (selector < i), "< %s >",
38356                       control_getdevicename(selector == i ? selected_device : playercontrolpointers[i]->deviceID));
38357         }
38358 
38359         if (selector < max_players)
38360         {
38361             _menutextm(1, selector + base + 1, 0, Tr("Press Start to apply, Up or Down to cancel"));
38362         }
38363 
38364         active_devices = 0;
38365         for (int i = 0; i < max_players; i++)
38366         {
38367             if (control_isvaliddevice(playercontrolpointers[i]->deviceID))
38368             {
38369                 _menutextm((selector == 4+i), 1 + active_devices, 0, Tr("Setup Player %i..."), i + 1);
38370                 ++active_devices;
38371             }
38372         }
38373 
38374         #if ANDROID
38375         if (savedata.is_touchpad_vibration_enabled)
38376         {
38377             _menutextm((selector == 8), 2 + active_devices, 0, Tr("Touchpad Vibration Enabled"));
38378         }
38379         else
38380         {
38381             _menutextm((selector == 8), 2 + active_devices, 0, Tr("Touchpad Vibration Disabled"));
38382         }
38383         _menutextm((selector == 9), 4 + active_devices, 0, Tr("Back"));
38384         #else
38385         _menutextm((selector == 8), 2 + active_devices, 0, Tr("Back"));
38386         #endif
38387 
38388         update((level != NULL), 0);
38389 
38390         if (bothnewkeys & FLAG_ESC)
38391         {
38392             quit = 1;
38393         }
38394         if (bothnewkeys & FLAG_MOVEUP)
38395         {
38396             --selector;
38397             if (SAMPLE_BEEP >= 0)
38398             {
38399                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38400             }
38401 
38402             // skip over invisible configuration entries for non-existent devices
38403             while (selector >= 4 && selector <= 7 && !control_isvaliddevice(selector - 4))
38404             {
38405                 --selector;
38406             }
38407 
38408             // skip over invisible device selection entries for non-existent players
38409             if (selector < 4 && selector >= max_players)
38410             {
38411                 selector = max_players - 1;
38412             }
38413         }
38414         if (bothnewkeys & FLAG_MOVEDOWN)
38415         {
38416             ++selector;
38417             if (SAMPLE_BEEP >= 0)
38418             {
38419                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38420             }
38421 
38422             // skip over invisible device selection entries for non-existent players
38423             if (selector < 4 && selector >= max_players)
38424             {
38425                 selector = 4;
38426             }
38427 
38428             // skip over invisible configuration entries for non-existent devices
38429             while (selector >= 4 && selector <= 7 && !control_isvaliddevice(selector - 4))
38430             {
38431                 ++selector;
38432             }
38433         }
38434         if (selector < 0)
38435         {
38436             selector = OPTIONS_NUM;
38437         }
38438         if (selector > OPTIONS_NUM)
38439         {
38440             selector = 0;
38441         }
38442         if (selector < 4 && (bothnewkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
38443         {
38444             selected_device = playercontrolpointers[selector]->deviceID;
38445         }
38446 
38447         if (bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
38448         {
38449             // Left/right only make sense for device reassignment
38450             if (selector >= 4 && !(bothnewkeys & FLAG_ANYBUTTON))
38451             {
38452                 continue;
38453             }
38454 
38455             if (SAMPLE_BEEP2 >= 0)
38456             {
38457                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38458             }
38459 
38460             switch (selector)
38461             {
38462             case 0:
38463             case 1:
38464             case 2:
38465             case 3:
38466                 if (bothnewkeys & FLAG_MOVELEFT)
38467                 {
38468                     do {
38469                         --selected_device;
38470                         if (selected_device < 0)
38471                         {
38472                             selected_device = MAX_DEVICES - 1;
38473                         }
38474                     } while (!control_isvaliddevice(selected_device));
38475                 }
38476                 else if (bothnewkeys & FLAG_MOVERIGHT)
38477                 {
38478                     do {
38479                         ++selected_device;
38480                         if (selected_device >= MAX_DEVICES)
38481                         {
38482                             selected_device = 0;
38483                         }
38484                     } while (!control_isvaliddevice(selected_device));
38485                 }
38486                 else // (bothnewkeys & FLAG_ANYBUTTON)
38487                 {
38488                     // assign selected device to player
38489                     safe_set_device(selector, selected_device, playercontrolpointers[selector]->deviceID);
38490                 }
38491                 break;
38492             case 4:
38493                 keyboard_setup(0);
38494                 break;
38495             case 5:
38496                 keyboard_setup(1);
38497                 break;
38498             case 6:
38499                 keyboard_setup(2);
38500                 break;
38501             case 7:
38502                 keyboard_setup(3);
38503                 break;
38504             #if ANDROID
38505             case 8:
38506                 savedata.is_touchpad_vibration_enabled = !savedata.is_touchpad_vibration_enabled;
38507                 break;
38508             #endif
38509             default:
38510                 quit = (bothnewkeys & FLAG_ANYBUTTON);
38511             }
38512         }
38513     }
38514     savesettings();
38515     bothnewkeys = 0;
38516     controloptionsMenu = 0;
38517 }
38518 
38519 
38520 
menu_options_sound()38521 void menu_options_sound()
38522 {
38523 
38524     int quit = 0;
38525     int selector = 0;
38526     int dir;
38527     int col1 = -8;
38528     int col2 = 6;
38529 
38530     soundoptionsMenu = 1;
38531     bothnewkeys = 0;
38532 
38533     while(!quit)
38534     {
38535         _menutextm(2, -5, 0, Tr("Sound Options"));
38536         _menutext((selector == 0), col1, -2, Tr("Sound Volume:"));
38537         _menutext((selector == 0), col2, -2, "%i", savedata.soundvol);
38538         _menutext((selector == 1), col1, -1, Tr("SFX Volume:"));
38539         _menutext((selector == 1), col2, -1, "%i", savedata.effectvol);
38540         _menutext((selector == 2), col1, 0, Tr("Music Volume:"));
38541         _menutext((selector == 2), col2, 0, "%i", savedata.musicvol);
38542         _menutext((selector == 3), col1, 1, Tr("BGM:"));
38543         _menutext((selector == 3), col2, 1, (savedata.usemusic ? Tr("Enabled") : Tr("Disabled")));
38544         _menutext((selector == 4), col1, 2, Tr("Show Titles:"));
38545         _menutext((selector == 4), col2, 2, (savedata.showtitles ? Tr("Yes") : Tr("No")));
38546         _menutextm((selector == 5), 5, 0, Tr("Back"));
38547 
38548         update((level != NULL), 0);
38549 
38550         if(bothnewkeys & FLAG_ESC)
38551         {
38552             quit = 1;
38553         }
38554         if(bothnewkeys & FLAG_MOVEUP)
38555         {
38556             --selector;
38557 
38558             if(SAMPLE_BEEP >= 0)
38559             {
38560                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38561             }
38562         }
38563         if(bothnewkeys & FLAG_MOVEDOWN)
38564         {
38565             ++selector;
38566 
38567             if(SAMPLE_BEEP >= 0)
38568             {
38569                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38570             }
38571         }
38572         if(selector < 0)
38573         {
38574             selector = 5;
38575         }
38576         if(selector > 5)
38577         {
38578             selector = 0;
38579         }
38580 
38581         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
38582         {
38583             dir = 0;
38584 
38585             if(bothnewkeys & FLAG_MOVELEFT)
38586             {
38587                 dir = -1;
38588             }
38589             else if(bothnewkeys & FLAG_MOVERIGHT)
38590             {
38591                 dir = 1;
38592             }
38593 
38594             if(SAMPLE_BEEP2 >= 0)
38595             {
38596                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38597             }
38598 
38599             switch(selector)
38600             {
38601             case 0:
38602                 savedata.soundvol += dir;
38603                 if(savedata.soundvol < 0)
38604                 {
38605                     savedata.soundvol = 0;
38606                 }
38607                 if(savedata.soundvol > 15)
38608                 {
38609                     savedata.soundvol = 15;
38610                 }
38611                 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
38612                 break;
38613             case 1:
38614                 savedata.effectvol += 4 * dir;
38615                 if(savedata.effectvol < 0)
38616                 {
38617                     savedata.effectvol = 0;
38618                 }
38619                 if(savedata.effectvol > 512)
38620                 {
38621                     savedata.effectvol = 512;
38622                 }
38623                 break;
38624             case 3:
38625                 if(!dir)
38626                 {
38627                     break;
38628                 }
38629                 if(!savedata.usemusic)
38630                 {
38631                     savedata.usemusic = 1;
38632                     music("data/music/remix", 1, 0);
38633                 }
38634                 else
38635                 {
38636                     savedata.usemusic = 0;
38637                     sound_close_music();
38638                 }
38639                 break;
38640             case 2:
38641                 savedata.musicvol += 4 * dir;
38642                 if(savedata.musicvol < 0)
38643                 {
38644                     savedata.musicvol = 0;
38645                 }
38646                 if(savedata.musicvol > 512)
38647                 {
38648                     savedata.musicvol = 512;
38649                 }
38650                 sound_volume_music(savedata.musicvol, savedata.musicvol);
38651                 break;
38652             case 4:
38653                 savedata.showtitles = !savedata.showtitles;
38654                 break;
38655             default:
38656                 quit = 1;
38657             }
38658         }
38659     }
38660     savesettings();
38661     bothnewkeys = 0;
38662     soundoptionsMenu = 0;
38663 }
38664 
menu_options_config()38665 void menu_options_config()     //  OX. Load from / save to default.cfg. Restore OpenBoR "factory" settings.
38666 {
38667     int quit = 0;
38668     int selector = 0;
38669     int saved = 0;
38670     int loaded = 0;
38671     int restored = 0;
38672 
38673     bothnewkeys = 0;
38674 
38675     while(!quit)
38676     {
38677         _menutextm(2, -5, 0, Tr("Configuration Settings"));
38678 
38679         if(saved == 1)
38680         {
38681             _menutextm((selector == 0), -3, 0, Tr("Save Settings To Default.cfg%s"), Tr("  Done!"));
38682         }
38683         else
38684         {
38685             _menutextm((selector == 0), -3, 0, Tr("Save Settings To Default.cfg%s"), "");
38686         }
38687 
38688         if(loaded == 1)
38689         {
38690             _menutextm((selector == 1), -2, 0, Tr("Load Settings From Default.cfg%s"), Tr("  Done!"));
38691         }
38692         else
38693         {
38694             _menutextm((selector == 1), -2, 0, Tr("Load Settings From Default.cfg%s"), "");
38695         }
38696 
38697         if(restored == 1)
38698         {
38699             _menutextm((selector == 2), -1, 0, Tr("Restore OpenBoR Defaults%s"), Tr("  Done!"));
38700         }
38701         else
38702         {
38703             _menutextm((selector == 2), -1, 0, Tr("Restore OpenBoR Defaults%s"), "");
38704         }
38705 
38706         _menutextm((selector == 3), 6, 0, Tr("Back"));
38707 
38708         update((level != NULL), 0);
38709 
38710         if(bothnewkeys & FLAG_ESC)
38711         {
38712             quit = 1;
38713         }
38714         if(bothnewkeys & FLAG_MOVEUP)
38715         {
38716             --selector;
38717 
38718             if(SAMPLE_BEEP >= 0)
38719             {
38720                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38721             }
38722         }
38723         if(bothnewkeys & FLAG_MOVEDOWN)
38724         {
38725             ++selector;
38726 
38727             if(SAMPLE_BEEP >= 0)
38728             {
38729                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38730             }
38731         }
38732 
38733         if(selector < 0)
38734         {
38735             selector = 3;
38736         }
38737         if(selector > 3)
38738         {
38739             selector = 0;
38740         }
38741 
38742         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
38743         {
38744 
38745             if(SAMPLE_BEEP2 >= 0)
38746             {
38747                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38748             }
38749 
38750             switch(selector)
38751             {
38752             case 0:
38753                 saveasdefault();
38754                 saved = 1;
38755                 break;
38756 
38757             case 1:
38758                 loadfromdefault();
38759                 //borShutdown(2, "\nSettings Loaded From Default.cfg. Restart Required.\n\n");
38760                 init_videomodes(0);
38761                 if(!video_set_mode(videomodes))
38762                 {
38763                     borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
38764                 }
38765                 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
38766                 sound_volume_music(savedata.musicvol, savedata.musicvol);
38767                 loaded = 1;
38768                 break;
38769             case 2:
38770                 clearsettings();
38771                 //borShutdown(2, "\nSettings Loaded From Default.cfg. Restart Required.\n\n");
38772                 init_videomodes(0);
38773                 if(!video_set_mode(videomodes))
38774                 {
38775                     borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
38776                 }
38777                 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
38778                 sound_volume_music(savedata.musicvol, savedata.musicvol);
38779                 restored = 1;
38780                 break;
38781             default:
38782                 quit = 1;
38783             }
38784         }
38785     }
38786     savesettings();
38787     bothnewkeys = 0;
38788 }
38789 
menu_options_debug()38790 void menu_options_debug()
38791 {
38792     #define MENU_POS_Y              -4
38793     #define MENU_ITEMS_MARGIN_Y     2
38794     #define COLUMN_1_POS_X          -11
38795     #define COLUMN_2_POS_X          COLUMN_1_POS_X + 14
38796     #define MENU_ITEM_FIRST_INDEX   0
38797 
38798     // Selections enumerator. All
38799     // selection items should be placed
38800     // here first.
38801     typedef enum
38802     {
38803         // First item can be
38804         // whatever we like,
38805         // but it must be set
38806         // to the MENU_ITEM_FIRST_INDEX
38807         // constant.
38808         ITEM_PERFORMANCE = MENU_ITEM_FIRST_INDEX,
38809 
38810         // Items between the first
38811         // and last can go in any
38812         // order.
38813         ITEM_POSITION,
38814         ITEM_COL_ATTACK,
38815         ITEM_COL_BODY,
38816         ITEM_COL_RANGE,
38817 
38818         // This is the "Back"
38819         // selection and should
38820         // always be last.
38821         ITEM_EXIT
38822     } e_selections;
38823 
38824     int pos_y;
38825     int quit                = 0;
38826     e_selections selector   = 0;
38827     bothnewkeys             = 0;
38828 
38829     while(!quit)
38830     {
38831         // Display menu title.
38832         _menutextm(2, MENU_POS_Y, 0, Tr("Debug Settings"));
38833 
38834         // Menu items.
38835         // Y position is controlled by a incremented integer.
38836         // Integer has a base value of the Menu title Y
38837         // plus static offset. Below each item, this Y value
38838         // increments by one. We can then add or remove
38839         // menu items here and the on screen position will
38840         // take care of itself without us needing to mess
38841         // with a bunch of hard constants.
38842 
38843         // Reset menu item position Y.
38844         pos_y = MENU_POS_Y + MENU_ITEMS_MARGIN_Y;
38845 
38846         _menutext((selector == ITEM_PERFORMANCE),    COLUMN_1_POS_X, pos_y, Tr("Performance:"));
38847         _menutext((selector == ITEM_PERFORMANCE),    COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_PERFORMANCE ? Tr("Enabled") : Tr("Disabled")));
38848         pos_y++;
38849 
38850         _menutext((selector == ITEM_POSITION),       COLUMN_1_POS_X, pos_y, Tr("Basic Properties:"));
38851         _menutext((selector == ITEM_POSITION),       COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_PROPERTIES ? Tr("Enabled") : Tr("Disabled")));
38852         pos_y++;
38853 
38854         _menutext((selector == ITEM_COL_ATTACK),     COLUMN_1_POS_X, pos_y, Tr("Collision Attack:"));
38855         _menutext((selector == ITEM_COL_ATTACK),     COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_COLLISION_ATTACK ? Tr("Enabled") : Tr("Disabled")));
38856         pos_y++;
38857 
38858         _menutext((selector == ITEM_COL_BODY),       COLUMN_1_POS_X, pos_y, Tr("Collision Body:"));
38859         _menutext((selector == ITEM_COL_BODY),       COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_COLLISION_BODY ? Tr("Enabled") : Tr("Disabled")));
38860         pos_y++;
38861 
38862         _menutext((selector == ITEM_COL_RANGE),      COLUMN_1_POS_X, pos_y, Tr("Range:"));
38863         _menutext((selector == ITEM_COL_RANGE),      COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_RANGE ? Tr("Enabled") : Tr("Disabled")));
38864         pos_y++;
38865 
38866         // Display exit title
38867         _menutextm((selector == ITEM_EXIT), pos_y + (MENU_ITEMS_MARGIN_Y), 0, Tr("Back"));
38868 
38869         // Run an engine update.
38870         update((level != NULL), 0);
38871 
38872         // If user presses up/down or esc, let's act accordingly.
38873         if(bothnewkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN | FLAG_ESC))
38874         {
38875             // Play beep if available.
38876             if(SAMPLE_BEEP >= 0)
38877             {
38878                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
38879             }
38880 
38881             // If user presses escape, then set quit
38882             // flag immediately. Else wise, increment
38883             // or decrement selector as needed.
38884             if(bothnewkeys & FLAG_ESC)
38885             {
38886                 quit = 1;
38887             }
38888             else if(bothnewkeys & FLAG_MOVEUP)
38889             {
38890                 // If we are at the top item, loop
38891                 // to last. Otherwise, move one up.
38892                 if(selector <= MENU_ITEM_FIRST_INDEX)
38893                 {
38894                     selector = ITEM_EXIT;
38895                 }
38896                 else
38897                 {
38898                     --selector;
38899                 }
38900             }
38901             else if(bothnewkeys & FLAG_MOVEDOWN)
38902             {
38903                 // If we are at the last item
38904                 // (which should be "back"), then
38905                 // loop back to first. Otherwise
38906                 // move one down.
38907                 if(selector >= ITEM_EXIT)
38908                 {
38909                     selector = MENU_ITEM_FIRST_INDEX;
38910                 }
38911                 else
38912                 {
38913                     ++selector;
38914                 }
38915             }
38916         }
38917 
38918 
38919         // Toggle selection value on left/right or
38920         // trigger button press.
38921         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
38922         {
38923             if(SAMPLE_BEEP2 >= 0)
38924             {
38925                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
38926             }
38927 
38928             // This is where menu items are executed.
38929             switch(selector)
38930             {
38931                 case ITEM_PERFORMANCE:
38932                     savedata.debuginfo ^= DEBUG_DISPLAY_PERFORMANCE;
38933                     break;
38934                 case ITEM_POSITION:
38935                     savedata.debuginfo ^= DEBUG_DISPLAY_PROPERTIES;
38936                     break;
38937                 case ITEM_COL_ATTACK:
38938                     savedata.debuginfo ^= DEBUG_DISPLAY_COLLISION_ATTACK;
38939                     break;
38940                 case ITEM_COL_BODY:
38941                     savedata.debuginfo ^= DEBUG_DISPLAY_COLLISION_BODY;
38942                     break;
38943                 case ITEM_COL_RANGE:
38944                     savedata.debuginfo ^= DEBUG_DISPLAY_RANGE;
38945                     break;
38946                 case ITEM_EXIT:
38947                     quit = 1;
38948             }
38949         }
38950     }
38951     savesettings();
38952     bothnewkeys = 0;
38953 
38954     #undef MENU_POS_Y
38955     #undef MENU_ITEMS_MARGIN_Y
38956     #undef COLUMN_1_POS_X
38957     #undef COLUMN_2_POS_X
38958     #undef MENU_ITEM_FIRST_INDEX
38959 }
38960 
38961 
menu_options_system()38962 void menu_options_system()
38963 {
38964     #define SYS_OPT_Y_POS -4
38965 
38966     enum {
38967         SYS_OPT_LOG,
38968         SYS_OPT_VSDAMAGE,
38969         SYS_OPT_CHEATS,
38970         SYS_OPT_DEBUG,
38971         SYS_OPT_CONFIG,
38972         SYS_OPT_PSP_CPUSPEED,
38973         SYS_OPT_BACK
38974     };
38975 
38976     int quit = 0;
38977     int selector = 0;
38978     int col1 = -11;
38979     int col2 = col1+14;
38980     int ex_labels = 0;
38981     int RET = SYS_OPT_BACK-1;
38982 
38983 #if PSP
38984     int dir = 0;
38985     int batteryPercentage = 0;
38986     int batteryLifeTime = 0;
38987     int externalPower = 0;
38988 #endif
38989 
38990     systemoptionsMenu = 1;
38991     bothnewkeys = 0;
38992     if (nodebugoptions) ex_labels = 1;
38993     RET -= ex_labels;
38994 
38995     while(!quit)
38996     {
38997         _menutextm(2, SYS_OPT_Y_POS-2, 0, Tr("System Options"));
38998 
38999         _menutext(0, col1, SYS_OPT_Y_POS, Tr("Total RAM:"));
39000         _menutext(0, col2, SYS_OPT_Y_POS, Tr("%s KB"), commaprint(getSystemRam(KBYTES)));
39001 
39002         _menutext(0, col1, SYS_OPT_Y_POS+1, Tr("Used RAM:"));
39003         _menutext(0, col2, SYS_OPT_Y_POS+1, Tr("%s KB"), commaprint(getUsedRam(KBYTES)));
39004 
39005         _menutext(0, col1, SYS_OPT_Y_POS+2, Tr("Max Players:"));
39006         _menutext(0, col2, SYS_OPT_Y_POS+2, Tr("%i"), levelsets[current_set].maxplayers);
39007 
39008         _menutext((selector == SYS_OPT_LOG), col1, SYS_OPT_Y_POS+4, Tr("File Logging:"));
39009         _menutext((selector == SYS_OPT_LOG), col2, SYS_OPT_Y_POS+4, (savedata.uselog ? Tr("Enabled") : Tr("Disabled")));
39010 
39011         _menutext((selector == SYS_OPT_VSDAMAGE), col1, SYS_OPT_Y_POS+5, Tr("Versus Damage:"), 0);
39012         if(versusdamage == 0)
39013         {
39014             _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Disabled by Module"));
39015         }
39016         else if(versusdamage == 1)
39017         {
39018             _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Enabled by Module"));
39019         }
39020         else
39021         {
39022             if(savedata.mode)
39023             {
39024                 _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Disabled"));    //Mode 1 - Players CAN'T attack each other
39025             }
39026             else
39027             {
39028                 _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Enabled"));    //Mode 2 - Players CAN attack each other
39029             }
39030         }
39031 
39032         _menutext((selector == SYS_OPT_CHEATS), col1, SYS_OPT_Y_POS+6, Tr("Cheats:"));
39033         _menutext((selector == SYS_OPT_CHEATS), col2, SYS_OPT_Y_POS+6, forcecheatsoff ? Tr("Disabled by Module") : (cheats ? Tr("On") : Tr("Off")));
39034         if(!nodebugoptions) _menutext((selector == SYS_OPT_DEBUG), col1, SYS_OPT_Y_POS+7, Tr("Debug Settings..."));
39035 
39036 #ifndef DC
39037 
39038         _menutext((selector == SYS_OPT_CONFIG-ex_labels), col1, SYS_OPT_Y_POS+8-ex_labels, Tr("Config Settings..."));
39039 
39040 #endif
39041 
39042 #if PSP
39043         externalPower = scePowerIsPowerOnline();
39044         _menutext((selector == SYS_OPT_PSP_CPUSPEED), col1, 4-ex_labels, Tr("CPU Speed:"));
39045         _menutext((selector == SYS_OPT_PSP_CPUSPEED), col2, 4-ex_labels, "%d MHz", scePowerGetCpuClockFrequency());
39046         if(!externalPower)
39047         {
39048             batteryPercentage = scePowerGetBatteryLifePercent();
39049             batteryLifeTime = scePowerGetBatteryLifeTime();
39050             _menutext(0, col1, 5-ex_labels, Tr("Battery:"));
39051             if(batteryPercentage < 0 || batteryLifeTime < 0)
39052             {
39053                 _menutext(0, col2, 5-ex_labels, Tr("Calculating..."));
39054             }
39055             else
39056             {
39057                 _menutext(0, col2, 5-ex_labels, "%d%% - %02d:%02d", batteryPercentage, batteryLifeTime / 60, batteryLifeTime - (batteryLifeTime / 60 * 60));
39058             }
39059         }
39060         else
39061         {
39062             _menutext(0, col1, 5-ex_labels, Tr("Charging:"));
39063             _menutext(0, col2, 5-ex_labels, Tr("%d%% AC Power"), scePowerGetBatteryLifePercent());
39064         }
39065         RET = 6-ex_labels;
39066 #endif
39067 
39068         _menutextm((selector == RET), SYS_OPT_Y_POS+11-ex_labels, 0, Tr("Back"));
39069 
39070         update((level != NULL), 0);
39071 
39072         if(bothnewkeys & FLAG_ESC)
39073         {
39074             quit = 1;
39075         }
39076         if(bothnewkeys & FLAG_MOVEUP)
39077         {
39078             --selector;
39079             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39080         }
39081         if(bothnewkeys & FLAG_MOVEDOWN)
39082         {
39083             ++selector;
39084             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39085         }
39086 
39087         if(selector < 0)
39088         {
39089             selector = RET;
39090         }
39091         if(selector > RET)
39092         {
39093             selector = 0;
39094         }
39095 
39096         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
39097         {
39098 
39099 #if PSP
39100             dir = 0;
39101             if(bothnewkeys & FLAG_MOVELEFT)
39102             {
39103                 dir = -1;
39104             }
39105             else if(bothnewkeys & FLAG_MOVERIGHT)
39106             {
39107                 dir = 1;
39108             }
39109 #endif
39110 
39111             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
39112 
39113                  if (selector==RET) quit = 1;
39114             else if (selector==SYS_OPT_LOG) savedata.uselog =  !savedata.uselog;
39115             else if (selector==SYS_OPT_VSDAMAGE)
39116             {
39117                 if(versusdamage > 1)
39118                 {
39119                     if(savedata.mode)
39120                     {
39121                         savedata.mode = 0;
39122                     }
39123                     else
39124                     {
39125                         savedata.mode = 1;
39126                     }
39127                 }
39128             }
39129             else if (selector==SYS_OPT_CHEATS) cheats = !cheats;
39130             else if (selector==SYS_OPT_DEBUG && !nodebugoptions) menu_options_debug();
39131 #ifndef DC
39132             else if (selector==SYS_OPT_CONFIG-ex_labels) menu_options_config();
39133 #endif
39134 
39135 #ifdef PSP
39136             else if (selector==SYS_OPT_PSP_CPUSPEED-ex_labels)
39137             {
39138                 savedata.pspcpuspeed += dir;
39139                 if(savedata.pspcpuspeed < 0)
39140                 {
39141                     savedata.pspcpuspeed = 2;
39142                 }
39143                 if(savedata.pspcpuspeed > 2)
39144                 {
39145                     savedata.pspcpuspeed = 0;
39146                 }
39147 
39148                 switch(savedata.pspcpuspeed)
39149                 {
39150                 case 0:
39151                     scePowerSetClockFrequency(222, 222, 111);
39152                     break;
39153                 case 1:
39154                     scePowerSetClockFrequency(266, 266, 133);
39155                     break;
39156                 case 2:
39157                     scePowerSetClockFrequency(333, 333, 166);
39158                     break;
39159                 }
39160             }
39161 #endif
39162             else quit = 1;
39163         }
39164     }
39165     savesettings();
39166     bothnewkeys = 0;
39167     systemoptionsMenu = 0;
39168 
39169     #undef SYS_OPT_Y_POS
39170 }
39171 
39172 
menu_options_video()39173 void menu_options_video()
39174 {
39175     int quit = 0;
39176     int selector = 0;
39177     int dir;
39178     int col1 = -15, col2 = 1;
39179 
39180     videooptionsMenu = 1;
39181     bothnewkeys = 0;
39182 
39183     while(!quit)
39184     {
39185         _menutextm(2, -5, 0, Tr("Video Options"));
39186         _menutext((selector == 0), col1, -3, Tr("Brightness:"));
39187         _menutext((selector == 0), col2, -3, "%i", savedata.brightness);
39188         _menutext((selector == 1), col1, -2, Tr("Gamma:"));
39189         _menutext((selector == 1), col2, -2, "%i", savedata.gamma);
39190         _menutext((selector == 2), col1, -1, Tr("Window Offset:"));
39191         _menutext((selector == 2), col2, -1, "%i", savedata.windowpos);
39192 
39193 #if OPENDINGUX
39194         _menutext((selector == 3), col1, 0, Tr("Display Mode:"));
39195         _menutext((selector == 3), col2, 0, savedata.fullscreen ? Tr("Full") : Tr("Window"));
39196         _menutextm((selector == 4), 6, 0, Tr("Back"));
39197         if(selector < 0)
39198         {
39199             selector = 4;
39200         }
39201         if(selector > 4)
39202         {
39203             selector = 0;
39204         }
39205 #endif
39206 
39207 #if DC || GP2X || VITA
39208         _menutextm((selector == 3), 6, 0, Tr("Back"));
39209         if(selector < 0)
39210         {
39211             selector = 3;
39212         }
39213         if(selector > 3)
39214         {
39215             selector = 0;
39216         }
39217 #endif
39218 
39219 #if WII
39220         _menutext((selector == 3), col1, 0, Tr("Display Mode:"));
39221         _menutext((selector == 3), col2, 0, (savedata.stretch ? Tr("Stretch to Screen") : Tr("Preserve Aspect Ratio")));
39222         _menutextm((selector == 4), 6, 0, Tr("Back"));
39223         if(selector < 0)
39224         {
39225             selector = 4;
39226         }
39227         if(selector > 4)
39228         {
39229             selector = 0;
39230         }
39231 #endif
39232 
39233 #if SDL
39234 #if !defined(GP2X) && !defined(OPENDINGUX)
39235         _menutext((selector == 3), col1, 0, Tr("Display Mode:"));
39236         _menutext((selector == 3), col2, 0, savedata.fullscreen ? Tr("Full") : Tr("Window"));
39237 
39238         _menutext((selector == 4), col1, 1, Tr("Video Backend:"));
39239         _menutext((selector == 4), col2, 1, (opengl ? Tr("OpenGL") : Tr("SDL")));
39240 
39241         _menutext((selector == 5), col1, 2, Tr("Scale:"));
39242 #ifdef ANDROID
39243         if(savedata.hwscale == 0)
39244 #else
39245 		if(savedata.fullscreen)
39246 #endif
39247         {
39248             _menutext((selector == 5), col2, 2, Tr("Automatic"));
39249         }
39250         else
39251         {
39252             _menutext((selector == 5), col2, 2, "%4.2fx - %ix%i", savedata.hwscale, (int)(videomodes.hRes * savedata.hwscale), (int)(videomodes.vRes * savedata.hwscale));
39253         }
39254 
39255         _menutext((selector == 6), col1, 3, Tr("Hardware Filter:"));
39256         {
39257             char *filterName;
39258             if (savedata.hwscale == 1.0 && !savedata.fullscreen)
39259                 filterName = "Disabled";
39260             else if (opengl)
39261                 filterName = "High Quality";
39262             else if (savedata.hwfilter)
39263                 filterName = "Simple";
39264             else
39265                 filterName = "Bilinear";
39266             _menutext((selector == 6), col2, 3, Tr(filterName));
39267         }
39268 
39269         _menutext((selector == 7), col1, 4, Tr("Software Filter:"));
39270         _menutext((selector == 7), col2, 4, ((savedata.hwscale >= 2.0 || savedata.fullscreen) ? Tr(GfxBlitterNames[savedata.swfilter]) : Tr("Disabled")));
39271 
39272         _menutext((selector == 8), col1, 5, Tr("VSync:"));
39273         _menutext((selector == 8), col2, 5, savedata.vsync ? "Enabled" : "Disabled");
39274 
39275         if(savedata.fullscreen)
39276         {
39277             _menutext((selector == 9), col1, 6, Tr("Fullscreen Type:"));
39278             _menutext((selector == 9), col2, 6, (savedata.stretch ? Tr("Stretch to Screen") : Tr("Preserve Aspect Ratio")));
39279         }
39280         else if(selector == 9)
39281         {
39282             selector = (bothnewkeys & FLAG_MOVEUP) ? 8 : 10;
39283         }
39284 
39285         _menutextm((selector == 10), 8, 0, Tr("Back"));
39286         if(selector < 0)
39287         {
39288             selector = 10;
39289         }
39290         if(selector > 10)
39291         {
39292             selector = 0;
39293         }
39294 #endif
39295 #endif
39296 
39297 #if PSP
39298         _menutext((selector == 3), col1, 0, Tr("Screen:"));
39299         _menutext((selector == 3), col2, 0, displayFormat[(int)videomodes.mode].name);
39300         _menutext((selector == 4), col1, 1, Tr("Filters:"));
39301         _menutext((selector == 4), col2, 1, filterName[(int)videomodes.filter]);
39302         _menutext((selector == 5), col1, 2, Tr("Display:"));
39303         _menutext((selector == 5), col2, 2, displayName[displayMode]);
39304         _menutext((selector >= 6 && selector <= 9), col1, 3, Tr("Overscan:"));
39305         _menutext((selector >= 6 && selector <= 9), col2 + 1.5, 3, ".");
39306         _menutext((selector >= 6 && selector <= 9), col2 + 3.5, 3, ".");
39307         _menutext((selector >= 6 && selector <= 9), col2 + 5.5, 3, ".");
39308         _menutext((selector == 6), col2, 3, "%02d", savedata.overscan[0]);
39309         _menutext((selector == 7), col2 + 2, 3, "%02d", savedata.overscan[1]);
39310         _menutext((selector == 8), col2 + 4, 3, "%02d", savedata.overscan[2]);
39311         _menutext((selector == 9), col2 + 6, 3, "%02d", savedata.overscan[3]);
39312         _menutextm((selector == 10), 6, 0, Tr("Back"));
39313         if(selector < 0)
39314         {
39315             selector = 10;
39316         }
39317         if(selector > 10)
39318         {
39319             selector = 0;
39320         }
39321 #endif
39322 
39323         update((level != NULL), 0);
39324 
39325         if(bothnewkeys & FLAG_ESC)
39326         {
39327             quit = 1;
39328         }
39329         if(bothnewkeys & FLAG_MOVEUP)
39330         {
39331             --selector;
39332             if(SAMPLE_BEEP >= 0)
39333             {
39334                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39335             }
39336         }
39337         if(bothnewkeys & FLAG_MOVEDOWN)
39338         {
39339             ++selector;
39340             if(SAMPLE_BEEP >= 0)
39341             {
39342                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39343             }
39344         }
39345         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
39346         {
39347             dir = 0;
39348 
39349             if(bothnewkeys & FLAG_MOVELEFT)
39350             {
39351                 dir = -1;
39352             }
39353             else if(bothnewkeys & FLAG_MOVERIGHT)
39354             {
39355                 dir = 1;
39356             }
39357 
39358             if(SAMPLE_BEEP2 >= 0)
39359             {
39360                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
39361             }
39362 
39363             switch(selector)
39364             {
39365             case 0:
39366                 savedata.brightness += 8 * dir;
39367                 if(savedata.brightness < -256)
39368                 {
39369                     savedata.brightness = -256;
39370                 }
39371                 if(savedata.brightness > 256)
39372                 {
39373                     savedata.brightness = 256;
39374                 }
39375                 vga_vwait();
39376                 set_color_correction(savedata.gamma, savedata.brightness);
39377                 break;
39378             case 1:
39379                 savedata.gamma += 8 * dir;
39380                 if(savedata.gamma < -256)
39381                 {
39382                     savedata.gamma = -256;
39383                 }
39384                 if(savedata.gamma > 256)
39385                 {
39386                     savedata.gamma = 256;
39387                 }
39388                 vga_vwait();
39389                 set_color_correction(savedata.gamma, savedata.brightness);
39390                 break;
39391             case 2:
39392                 savedata.windowpos += dir;
39393                 if(savedata.windowpos < -2)
39394                 {
39395                     savedata.windowpos = -2;
39396                 }
39397                 if(savedata.windowpos > 20)
39398                 {
39399                     savedata.windowpos = 20;
39400                 }
39401                 break;
39402 #if SDL || PSP || WII
39403             case 3:
39404 #if OPENDINGUX
39405                 video_fullscreen_flip();
39406                 break;
39407 #endif
39408 
39409 #if WII
39410                 //video_fullscreen_flip();
39411                 video_stretch((savedata.stretch ^= 1));
39412                 break;
39413 #endif
39414 
39415 #if PSP
39416                 if(videoMode == 0 || videoMode == 1)
39417                 {
39418                     // 320x240 or 480x272
39419                     videomodes.mode += dir;
39420                     if(videomodes.mode > PSP_DISPLAY_FORMATS - 1)
39421                     {
39422                         videomodes.mode = 0;
39423                     }
39424                     if(videomodes.mode < 0)
39425                     {
39426                         videomodes.mode = PSP_DISPLAY_FORMATS - 1;
39427                     }
39428                     video_set_mode(videomodes);
39429                 }
39430                 break;
39431 
39432             case 4:
39433                 if(videoMode == 0 || videoMode == 1)
39434                 {
39435                     // 320x240 or 480x272
39436                     videomodes.filter += dir;
39437                     if(videomodes.filter > PSP_DISPLAY_FILTERS - 1)
39438                     {
39439                         videomodes.filter = 0;
39440                     }
39441                     if(videomodes.filter < 0)
39442                     {
39443                         videomodes.filter = PSP_DISPLAY_FILTERS - 1;
39444                     }
39445                     savedata.swfilter = videomodes.filter;
39446                     video_set_mode(videomodes);
39447                 }
39448                 break;
39449 
39450             case 5:
39451                 displayMode += dir;
39452                 if(displayMode > PSP_DISPLAY_MODES - 1)
39453                 {
39454                     displayMode = 0;
39455                 }
39456                 if(displayMode < 0)
39457                 {
39458                     displayMode = PSP_DISPLAY_MODES - 1;
39459                 }
39460                 if(displayMode)
39461                 {
39462                     setGraphicsTVOverScan(savedata.overscan[0], savedata.overscan[1], savedata.overscan[2], savedata.overscan[3]);
39463                 }
39464                 else
39465                 {
39466                     setGraphicsTVOverScan(0, 0, 0, 0);
39467                 }
39468                 savedata.usetv = displayMode;
39469                 disableGraphics();
39470                 initGraphics(savedata.usetv, videomodes.pixel);
39471                 video_set_mode(videomodes);
39472                 break;
39473             case 6:
39474             case 7:
39475             case 8:
39476             case 9:
39477                 savedata.overscan[selector - 8] += dir;
39478                 if(savedata.overscan[selector - 8] > 99)
39479                 {
39480                     savedata.overscan[selector - 8] = 0;
39481                 }
39482                 if(savedata.overscan[selector - 8] < 0)
39483                 {
39484                     savedata.overscan[selector - 8] = 99;
39485                 }
39486                 if(displayMode)
39487                 {
39488                     setGraphicsTVOverScan(savedata.overscan[0], savedata.overscan[1], savedata.overscan[2], savedata.overscan[3]);
39489                     video_set_mode(videomodes);
39490                 }
39491                 break;
39492 #endif
39493 #endif
39494 
39495 
39496 #if SDL
39497 #if !defined(GP2X) && !defined(OPENDINGUX)
39498                 video_fullscreen_flip();
39499                 break;
39500             case 4:
39501                 savedata.usegl = !savedata.usegl;
39502                 video_set_mode(videomodes);
39503                 set_color_correction(savedata.gamma, savedata.brightness);
39504                 break;
39505             case 5:
39506 #ifndef ANDROID
39507                 if(savedata.fullscreen)
39508                 {
39509                     break;
39510                 }
39511                 savedata.hwscale += dir * 0.25;
39512                 if(savedata.hwscale < 0.25)
39513                 {
39514                     savedata.hwscale = 0.25;
39515                 }
39516                 if(savedata.hwscale > 4.00)
39517                 {
39518                     savedata.hwscale = 4.00;
39519                 }
39520                 video_set_mode(videomodes);
39521 #else
39522                 savedata.hwscale += dir * 0.25;
39523                 if(savedata.hwscale < 0.0)
39524                 {
39525                     savedata.hwscale = 0.0;
39526                 }
39527                 if(savedata.hwscale > 4.00)
39528                 {
39529                     savedata.hwscale = 4.00;
39530                 }
39531 #endif
39532                 break;
39533             case 6:
39534 #ifndef ANDROID
39535                 if (opengl || (!savedata.fullscreen && savedata.hwscale == 1.0))
39536                 {
39537                     break;
39538                 }
39539 #endif
39540                 savedata.hwfilter += dir;
39541                 if(savedata.hwfilter < 0)
39542                 {
39543                     savedata.hwfilter = 1;
39544                 }
39545                 if(savedata.hwfilter > 1)
39546                 {
39547                     savedata.hwfilter = 0;
39548                 }
39549                 video_set_mode(videomodes);
39550 				break;
39551             case 7:
39552                 if(!savedata.fullscreen && savedata.hwscale < 2.0)
39553                 {
39554                     break;
39555                 }
39556                 videomodes.filter += dir;
39557                 if(videomodes.filter > BLITTER_MAX - 1)
39558                 {
39559                     videomodes.filter = 0;
39560                 }
39561                 if(videomodes.filter < 0)
39562                 {
39563                     videomodes.filter = BLITTER_MAX - 1;
39564                 }
39565                 savedata.swfilter = videomodes.filter;
39566                 memset(pDeltaBuffer, 0x00, 1244160);
39567 				video_set_mode(videomodes);
39568                 break;
39569             case 8:
39570                 savedata.vsync = !savedata.vsync;
39571                 video_set_mode(videomodes);
39572                 break;
39573             case 9:
39574                 video_stretch((savedata.stretch ^= 1));
39575                 break;
39576 #endif
39577 #endif
39578             default:
39579                 quit = 1;
39580             }
39581         }
39582     }
39583     savesettings();
39584     bothnewkeys = 0;
39585     videooptionsMenu = 0;
39586 }
39587 
39588 
menu_options()39589 void menu_options()
39590 {
39591     #define TOT_CHEATS          4 // Kratus (20-04-21) increase +1 line to the multihit glitch option
39592     #define OPT_Y_POS          -1
39593     #define OPT_X_POS          -7
39594     #define CHEAT_PAUSE_POSY    4 // Kratus (20-04-21) increase +1 line to the multihit glitch option
39595 
39596     typedef enum {
39597         VIDEO_OPTION,
39598         SOUND_OPTION,
39599         CONTROL_OPTION,
39600         SYSTEM_OPTION,
39601 
39602         LIVES_CHEAT,
39603         CREDITS_CHEAT,
39604         HEALTH_CHEAT,
39605         MULTIHIT_CHEAT, // Kratus (20-04-21) add the multihit glitch option
39606 
39607         END_OPTION
39608     } e_selector;
39609 
39610     int quit = 0;
39611     int y_offset = OPT_Y_POS;
39612     int BACK_OPTION = END_OPTION-TOT_CHEATS;
39613     int cheat_opt_offset = 0;
39614     e_selector selector = VIDEO_OPTION;
39615 
39616     optionsMenu = 1;
39617     bothnewkeys = 0;
39618 
39619     if (cheats && !forcecheatsoff)
39620     {
39621         if(level != NULL && _pause > 0) y_offset += CHEAT_PAUSE_POSY;
39622         y_offset -= TOT_CHEATS;
39623         cheat_opt_offset += 1;
39624         BACK_OPTION += TOT_CHEATS;
39625     }
39626 
39627     while(!quit)
39628     {
39629         if (!cheats || forcecheatsoff) _menutextm(2, y_offset-1, 0, Tr("Options")); else _menutextm(2, y_offset-1, 0, Tr("Cheat Options"));
39630 
39631         _menutextm((selector == VIDEO_OPTION), y_offset+VIDEO_OPTION, 0, Tr("Video Options..."));
39632         _menutextm((selector == SOUND_OPTION), y_offset+SOUND_OPTION, 0, Tr("Sound Options..."));
39633         _menutextm((selector == CONTROL_OPTION), y_offset+CONTROL_OPTION, 0, Tr("Control Options..."));
39634         _menutextm((selector == SYSTEM_OPTION), y_offset+SYSTEM_OPTION, 0, Tr("System Options..."));
39635 
39636         if (cheats && !forcecheatsoff)
39637         {
39638             _menutext((selector == LIVES_CHEAT), OPT_X_POS, y_offset+cheat_opt_offset+LIVES_CHEAT, (livescheat)?Tr("Infinite Lives On"):Tr("Infinite Lives Off"));
39639             _menutext((selector == CREDITS_CHEAT), OPT_X_POS, y_offset+cheat_opt_offset+CREDITS_CHEAT, (creditscheat)?Tr("Infinite Credits On"):Tr("Infinite Credits Off"));    // Enemies fall/don't fall down when you respawn
39640             _menutext((selector == HEALTH_CHEAT), OPT_X_POS, y_offset+cheat_opt_offset+HEALTH_CHEAT, (healthcheat)?Tr("Infinite Health On"):Tr("Infinite Health Off"));    // Enemies fall/don't down when you respawn
39641             _menutext((selector == MULTIHIT_CHEAT), OPT_X_POS, y_offset+cheat_opt_offset+MULTIHIT_CHEAT, (multihitcheat)?Tr("Multihit Glitch On"):Tr("Multihit Glitch Off"));    // Kratus (20-04-21) change the multihit glitch option on/off
39642         }
39643 
39644         _menutextm((selector == BACK_OPTION), y_offset+cheat_opt_offset+BACK_OPTION+2, 0, Tr("Back"));
39645 
39646         update((level != NULL), 0);
39647 
39648         if(bothnewkeys & FLAG_ESC)
39649         {
39650             quit = 1;
39651         }
39652         if(bothnewkeys & FLAG_MOVEUP)
39653         {
39654             if(selector <= VIDEO_OPTION)
39655             {
39656                 selector = BACK_OPTION;
39657             }
39658             else --selector;
39659 
39660             if(SAMPLE_BEEP >= 0)
39661             {
39662                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39663             }
39664         }
39665         if(bothnewkeys & FLAG_MOVEDOWN)
39666         {
39667             ++selector;
39668             if(selector > BACK_OPTION)
39669             {
39670                 selector = VIDEO_OPTION;
39671             }
39672 
39673             if(SAMPLE_BEEP >= 0)
39674             {
39675                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39676             }
39677         }
39678         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
39679         {
39680 
39681             if(SAMPLE_BEEP2 >= 0)
39682             {
39683                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
39684             }
39685 
39686                 if(selector==BACK_OPTION) quit = 1;
39687            else if(selector==VIDEO_OPTION) menu_options_video();
39688            else if(selector==SOUND_OPTION) menu_options_sound();
39689            else if(selector==CONTROL_OPTION) menu_options_input();
39690            else if(selector==SYSTEM_OPTION)
39691            {
39692                 menu_options_system();
39693                 if (cheats && !forcecheatsoff)
39694                 {
39695                     y_offset = OPT_Y_POS-TOT_CHEATS;
39696                     if(level != NULL && _pause > 0) y_offset += CHEAT_PAUSE_POSY;
39697                     cheat_opt_offset = 1;
39698                     BACK_OPTION = END_OPTION-TOT_CHEATS+TOT_CHEATS;
39699                 }
39700                 else
39701                 {
39702                     y_offset = OPT_Y_POS;
39703                     cheat_opt_offset = 0;
39704                     BACK_OPTION = END_OPTION-TOT_CHEATS;
39705                 }
39706            }
39707            else if(selector==LIVES_CHEAT) livescheat = !livescheat;
39708            else if(selector==CREDITS_CHEAT) creditscheat = !creditscheat;
39709            else if(selector==HEALTH_CHEAT) healthcheat = !healthcheat;
39710            else if(selector==MULTIHIT_CHEAT) multihitcheat = !multihitcheat; // Kratus (20-04-21) selector for the multihit glitch option
39711            else quit = 1;
39712         }
39713     }
39714     savesettings();
39715     if(_pause == 1)
39716     {
39717         _pause = 2;
39718     }
39719     bothnewkeys = 0;
39720     optionsMenu = 0;
39721 
39722     #undef TOT_CHEATS
39723     #undef OPT_Y_POS
39724     #undef OPT_X_POS
39725     #undef CHEAT_PAUSE_POSY
39726 }
39727 
39728 // ----------------------------------------------------------------------------
39729 
39730 
openborMain(int argc,char ** argv)39731 void openborMain(int argc, char **argv)
39732 {
39733     sprite_map = NULL;
39734     int quit = 0;
39735     int relback = 1;
39736     int selector = 0;
39737     u32 introtime = 0;
39738     int started = 0;
39739     char tmpBuff[MAX_BUFFER_LEN] = {""};
39740     int players[MAX_PLAYERS] = {0, 0, 0, 0};
39741     int i;
39742     int argl;
39743 
39744     printf("OpenBoR %s, Compile Date: " __DATE__ "\n\n", VERSION);
39745 
39746     if(argc > 1)
39747     {
39748         argl = strlen(argv[1]);
39749         if(argl > 14 && !memcmp(argv[1], "offscreenkill=", 14))
39750         {
39751             DEFAULT_OFFSCREEN_KILL = getValidInt((char *)argv[1] + 14, "", "");
39752         }
39753         if(argl > 14 && !memcmp(argv[1], "showfilesused=", 14))
39754         {
39755             printFileUsageStatistics = getValidInt((char *)argv[1] + 14, "", "");
39756         }
39757     }
39758 
39759 
39760     modelcmdlist = createModelCommandList();
39761     modelstxtcmdlist = createModelstxtCommandList();
39762     levelcmdlist = createLevelCommandList();
39763     levelordercmdlist = createLevelOrderCommandList();
39764     createModelList();
39765 
39766     // Load necessary components.
39767     printf("Game Selected: %s\n\n", packfile);
39768     loadsettings();
39769     startup();
39770 	bothnewkeys = 0;
39771 
39772     if(skiptoset < 0)
39773     {
39774 
39775         // New alternative background path for PSP
39776         if(custBkgrds != NULL)
39777         {
39778             strcpy(tmpBuff, custBkgrds);
39779             strcat(tmpBuff, "logo");
39780             load_background(tmpBuff);
39781         }
39782         else
39783         {
39784             load_cached_background("data/bgs/logo");
39785         }
39786 
39787         while(_time < GAME_SPEED * 6 && !(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC)))
39788         {
39789             update(0, 0);
39790         }
39791 
39792         music("data/music/remix", 1, 0);
39793 
39794         // New alternative scene path for PSP
39795         if(custScenes != NULL)
39796         {
39797             strcpy(tmpBuff, custScenes);
39798             strcat(tmpBuff, "logo.txt");
39799             playscene(tmpBuff);
39800         }
39801         else
39802         {
39803             playscene("data/scenes/logo.txt");
39804         }
39805     }
39806     clearscreen(background);
39807 
39808     while(!quit)
39809     {
39810         if(skiptoset < 0 && !(goto_mainmenu_flag&8))
39811         {
39812             if(_time >= introtime)
39813             {
39814                 // New alternative scene path for PSP
39815                 if(custScenes != NULL)
39816                 {
39817                     strcpy(tmpBuff, custScenes);
39818                     strcat(tmpBuff, "intro.txt");
39819                     playscene(tmpBuff);
39820                 }
39821                 else
39822                 {
39823                     playscene("data/scenes/intro.txt");
39824                 }
39825                 update(0, 0);
39826                 introtime = _time + GAME_SPEED * 20;
39827                 relback = 1;
39828                 started = 0;
39829             }
39830 
39831             if(bothnewkeys & FLAG_ESC)
39832             {
39833                 quit = 1;
39834             }
39835         }
39836         else
39837         {
39838             started = 1;
39839             relback = 0;
39840         }
39841 
39842         if (goto_mainmenu_flag != 0) goto_mainmenu_flag = 0;
39843         if(!started)
39844         {
39845             if((_time % GAME_SPEED) < (GAME_SPEED / 2))
39846             {
39847                 _menutextm(0, 0, 0, Tr("PRESS START"));
39848             }
39849             if(bothnewkeys & (FLAG_ANYBUTTON))
39850             {
39851                 started = 1;
39852                 relback = 1;
39853             }
39854         }
39855         else if(skiptoset >= 0)
39856         {
39857             loadGameFile();
39858             playgame(players, useSet >= 0 ? useSet : skiptoset, useSave);
39859         }
39860         else
39861         {
39862             _menutextm((selector == 0), 2, 0, Tr("Start Game"));
39863             _menutextm((selector == 1), 3, 0, Tr("Options"));
39864             _menutextm((selector == 2), 4, 0, Tr("How To Play"));
39865             _menutextm((selector == 3), 5, 0, Tr("Hall Of Fame"));
39866             _menutextm((selector == 4), 6, 0, Tr("Quit"));
39867             if(selector < 0)
39868             {
39869                 selector = 4;
39870             }
39871             if(selector > 4)
39872             {
39873                 selector = 0;
39874             }
39875 
39876             if(bothnewkeys)
39877             {
39878                 introtime = _time + GAME_SPEED * 20;
39879             }
39880 
39881             if(bothnewkeys & FLAG_MOVEUP)
39882             {
39883                 --selector;
39884                 if(SAMPLE_BEEP >= 0)
39885                 {
39886                     sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39887                 }
39888             }
39889             if(bothnewkeys & FLAG_MOVEDOWN)
39890             {
39891                 ++selector;
39892                 if(SAMPLE_BEEP >= 0)
39893                 {
39894                     sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
39895                 }
39896             }
39897             if(bothnewkeys & (FLAG_ANYBUTTON))
39898             {
39899                 if(SAMPLE_BEEP2 >= 0)
39900                 {
39901                     sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
39902                 }
39903                 switch(selector)
39904                 {
39905                 case 0:
39906                     for(i = 0; i < MAX_PLAYERS; i++)
39907                     {
39908                         players[i] = player[i].newkeys & (FLAG_ANYBUTTON);
39909                     }
39910                     relback = choose_mode(players);
39911                     if(relback)
39912                     {
39913                         started = 0;
39914                     }
39915                     break;
39916                 case 1:
39917                     menu_options();
39918                     break;
39919                 case 2:
39920                 {
39921                     int previousLoop = musicloop;
39922                     char previousMusic[sizeof(currentmusic)];
39923 
39924                     memset(previousMusic, 0, sizeof(previousMusic));
39925                     strcpy(previousMusic, currentmusic);
39926 
39927                     if(custScenes != NULL)
39928                     {
39929                         strcpy(tmpBuff, custScenes);
39930                         strcat(tmpBuff, "howto.txt");
39931                         playscene(tmpBuff);
39932                     }
39933                     else
39934                     {
39935                         playscene("data/scenes/howto.txt");
39936                     }
39937                     if(stricmp(previousMusic, currentmusic) != 0)
39938                     {
39939                         music(previousMusic, previousLoop, 0);
39940                     }
39941                     relback = 1;
39942                     break;
39943                 }
39944                 case 3:
39945                     hallfame(0);
39946                     relback = 1;
39947                     break;
39948                 default:
39949                     quit = 1;
39950                     break;
39951                 }
39952                 introtime = _time + GAME_SPEED * 20;
39953             }
39954         }
39955         if(relback)
39956         {
39957             if(started)
39958             {
39959                 menuScreen  = 1;
39960                 titleScreen = 0;
39961                 if(custBkgrds != NULL)
39962                 {
39963                     strcpy(tmpBuff, custBkgrds);
39964                     strcat(tmpBuff, "titleb");
39965                     load_background(tmpBuff);
39966                 }
39967                 else
39968                 {
39969                     load_cached_background("data/bgs/titleb");
39970                 }
39971             }
39972             else
39973             {
39974                 menuScreen  = 0;
39975                 titleScreen = 1;
39976                 if(custBkgrds != NULL)
39977                 {
39978                     strcpy(tmpBuff, custBkgrds);
39979                     strcat(tmpBuff, "title");
39980                     load_background(tmpBuff);
39981                 }
39982                 else
39983                 {
39984                     load_cached_background("data/bgs/title");
39985                 }
39986             }
39987 
39988             if(!sound_query_music(NULL, NULL))
39989             {
39990                 music("data/music/remix", 1, 0);
39991             }
39992             relback = 0;
39993         }
39994         update(0, 0);
39995     }
39996     borShutdown(0, DEFAULT_SHUTDOWN_MESSAGE);
39997 }
39998 
is_cheat_actived()39999 int is_cheat_actived()
40000 {
40001     if(!cheats)
40002     {
40003         return 0;
40004     }
40005     else
40006     {
40007         if(!forcecheatsoff)
40008         {
40009             return 1;
40010         }
40011         else
40012         {
40013             return 0;
40014         }
40015     }
40016 }
40017 
40018 #undef GET_ARG
40019 #undef GET_ARG_LEN
40020 #undef GET_ARGP
40021 #undef GET_ARGP_LEN
40022 #undef GET_INT_ARG
40023 #undef GET_FLOAT_ARG
40024 #undef GET_INT_ARGP
40025 #undef GET_FLOAT_ARGP
40026