1 /*
2  * OpenBOR - http://www.chronocrash.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) 2004 - 2013 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 
39 s_set_entry *levelsets = NULL;
40 int        num_difficulties;
41 
42 int no_cmd_compatible = 0;
43 
44 int		skiptoset = -1;
45 //when there are more entities than this, those with lower priority will be erased
46 int spawnoverride = 999999;
47 int maxentities = 999999;
48 
49 int	global_model = -1;
50 #define global_model_scripts ((global_model>=0 && model_cache[global_model].model)?model_cache[global_model].model->scripts:NULL)
51 
52 s_level            *level               = NULL;
53 s_filestream *filestreams = NULL;
54 int numfilestreams = 0;
55 s_screen           *vscreen             = NULL;
56 s_screen           *background          = NULL;
57 s_videomodes        videomodes;
58 int sprite_map_max_items = 0;
59 int cache_map_max_items = 0;
60 
61 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.
62 List *modelcmdlist = NULL;
63 List *modelstxtcmdlist = NULL;
64 List *levelcmdlist = NULL;
65 List *levelordercmdlist = NULL;
66 
67 int atkchoices[MAX_ANIS]; //tempory values for ai functions, should be well enough LOL
68 
69 //see types.h
70 const s_drawmethod plainmethod =
71 {
72     .table      = NULL,
73     .fp         = NULL,
74     .fillcolor  = 0,
75     .flag       = 1,
76     .alpha      = -1,
77     .remap      = -1,
78     .flipx      = 0,
79     .flipy      = 0,
80     .transbg    = 0,
81     .fliprotate = 0,
82     .rotate     = 0,
83     .scalex     = 256,
84     .scaley     = 256,
85     .shiftx     = 0,
86     .centerx    = 0,
87     .centery    = 0,
88     .xrepeat    = 1,
89     .yrepeat    = 1,
90     .xspan      = 0,
91     .yspan      = 0,
92     .channelr   = 255,
93     .channelg   = 255,
94     .channelb   = 255,
95     .tintmode   = 0,
96     .tintcolor  = 0,
97     .clipx      = 0,
98     .clipy      = 0,
99     .clipw      = 0,
100     .cliph      = 0,
101     .water = {{.beginsize = 0.0}, {.endsize = 0.0}, 0, {.wavespeed = 0}, 0}
102 };
103 
104 const s_defense default_defense =
105 {
106     .factor         = 1.f,
107     .pain           = 0.f,
108     .knockdown      = 1.f,
109     .blockpower     = 0.f,
110     .blockthreshold = 0.f,
111     .blockratio     = 0.f,
112     .blocktype      = BLOCK_TYPE_MP_FIRST
113 };
114 
115 const s_hitbox empty_collision_coords = { .x      = 0,
116                                             .y      = 0,
117                                             .width  = 0,
118                                             .height = 0,
119                                             .z1     = 0,
120                                             .z2     = 0};
121 
122 const s_collision_body empty_body =   {     .coords     = NULL,
123                                             .index      = 0,
124                                             .defense    = NULL,
125                                             .tag        = 0
126                                     };
127 
128 // Recursive damage (dot).
129 const s_damage_recursive empty_recursive = {    .force  = 0,
130                                                 .index  = 0,
131                                                 .mode   = 0,
132                                                 .rate   = 0,
133                                                 .time   = 0};
134 
135 // unknockdown attack
136 const s_collision_attack emptyattack =
137 {
138     .attack_drop        = 0,
139     .attack_force       = 0,
140     .attack_type        = 0,
141     .blast              = 0,
142     .blockflash         = -1,
143     .blocksound         = -1,
144     .coords             = NULL,
145     .counterattack      = 0,
146     .damage_on_landing  = 0,
147     .dropv              = { .x = 0,
148                             .y = 0,
149                             .z = 0},
150     .force_direction    = DIRECTION_ADJUST_NONE,
151     .forcemap           = 0,
152     .freeze             = 0,
153     .freezetime         = 0,
154     .grab               = 0,
155     .grab_distance      = 0,
156     .guardcost          = 0,
157     .hitflash           = -1,
158     .hitsound           = -1,
159     .jugglecost         = 0,
160     .maptime            = 0,
161     .no_block           = 0,
162     .no_flash           = 0,
163     .no_kill            = 0,
164     .no_pain            = 0,
165     .otg                = OTG_NONE,
166     .pain_time          = 0,
167     .pause_add          = 0,
168     .recursive          = NULL,
169     .seal               = 0,
170     .sealtime           = 0,
171     .staydown           = { .rise               = 0,
172                             .riseattack         = 0,
173                             .riseattack_stall   = 0},
174     .steal              = 0,
175     .tag                = 0
176 };
177 
178 s_axis_f default_model_dropv =
179 {
180     /* Default values for knockdown velocity */
181 
182     .x = 1.2f,
183     .y = 3.f,
184     .z = 0.f
185 };
186 
187 //default values
188 float default_level_maxtossspeed = 100.0f;
189 float default_level_maxfallspeed = -6.0f;
190 float default_level_gravity = -0.1f;
191 
192 float default_model_jumpheight = 4.0f;
193 float default_model_jumpspeed = -1;
194 float default_model_grabdistance = 36.0f;
195 
196 // AI attack debug stuff for development purpose,
197 // Don't open them to modders yet
198 float move_noatk_factor = 3.0f;
199 float group_noatk_factor = 0.01f;
200 float agg_noatk_factor = 0.0f;
201 float min_noatk_chance = 0.0f;
202 float max_noatk_chance = 0.6f;
203 float offscreen_noatk_factor = 0.5f;
204 float noatk_duration = 0.75f;
205 
206 char                *custScenes = NULL;
207 char                *custBkgrds = NULL;
208 char                *custLevels = NULL;
209 char                *custModels = NULL;
210 char                rush_names[2][MAX_NAME_LEN];
211 char				skipselect[MAX_PLAYERS][MAX_NAME_LEN];
212 char                branch_name[MAX_NAME_LEN + 1];  // Used for branches
213 char                allowselect_args[MAX_ALLOWSELECT_LEN]; // stored allowselect players
214 int					useSave = 0;
215 int					useSet = -1;
216 unsigned char       pal[MAX_PAL_SIZE] = {""};
217 int                 blendfx[MAX_BLENDINGS] = {0, 1, 0, 0, 0, 0};
218 char                blendfx_is_set = 0;
219 int                 fontmonospace[MAX_FONTS] = {0, 0, 0, 0, 0, 0, 0, 0};
220 int                 fontmbs[MAX_FONTS] = {0, 0, 0, 0, 0, 0, 0, 0};
221 
222 // move all blending effects here
223 unsigned char      *blendings[MAX_BLENDINGS] = {NULL, NULL, NULL, NULL, NULL, NULL} ;
224 // function pointers to create the tables
225 palette_table_function blending_table_functions[MAX_BLENDINGS] = {palette_table_screen, palette_table_multiply, palette_table_overlay, palette_table_hardlight, palette_table_dodge, palette_table_half};
226 blend_table_function blending_table_functions16[MAX_BLENDINGS] = {create_screen16_tbl, create_multiply16_tbl, create_overlay16_tbl, create_hardlight16_tbl, create_dodge16_tbl, create_half16_tbl};
227 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};
228 
229 int                 current_set = 0;
230 int                 current_level = 0;
231 int                 current_stage = 1;
232 
233 int					timevar;
234 float               bgtravelled;
235 float               vbgtravelled;
236 int                 traveltime;
237 int                 texttime;
238 int					timetoshow;
239 int					showgo;
240 float               advancex;
241 float               advancey;
242 
243 float               scrolldx;                       // advancex changed previous loop
244 float               scrolldy;                       // advancey .....................
245 float               scrollminz;                     // Limit level z-scroll
246 float               scrollmaxz;
247 float               blockade;                    // Limit x scroll back
248 float				scrollminx;
249 float				scrollmaxx;
250 
251 s_lasthit           lasthit;  //Last collision variables. 2013-12-15, moved to struct.
252 
253 int					combodelay = GAME_SPEED / 2;		// avoid annoying 112112... infinite combo
254 
255 //Use for gfx_shadow
256 s_axis_i_2d light = {   .x = 128,
257                         .y = 64};
258 
259 int                 shadowcolor = 0;
260 int                 shadowalpha = BLEND_MULTIPLY + 1;
261 int                 shadowopacity = 255;
262 
263 u64 totalram = 0;
264 u64 usedram = 0;
265 u64 freeram = 0;
266 u32 interval = 0;
267 //extern u64 seed;
268 
269 int                 SAMPLE_GO			= -1;
270 int                 SAMPLE_BEAT			= -1;
271 int                 SAMPLE_BLOCK		= -1;
272 int                 SAMPLE_INDIRECT		= -1;
273 int                 SAMPLE_GET			= -1;
274 int                 SAMPLE_GET2			= -1;
275 int                 SAMPLE_FALL			= -1;
276 int                 SAMPLE_JUMP			= -1;
277 int                 SAMPLE_PUNCH		= -1;
278 int                 SAMPLE_1UP			= -1;
279 int                 SAMPLE_TIMEOVER		= -1;
280 int                 SAMPLE_BEEP			= -1;
281 int                 SAMPLE_BEEP2		= -1;
282 int                 SAMPLE_BIKE			= -1;
283 int                 SAMPLE_PAUSE		= -1;
284 
285 // 2016-11-01
286 // Caskey, Damon V.
287 //
288 // Collision indexes. Only using globals while
289 // building multiple collision box support.
290 // Once we get this working, variables should
291 // be moved into a structure. Globals BAD!
292 int                 max_collisons       = MAX_COLLISIONS;
293 int                 *collisions         = NULL;
294 
295 
296 int                 max_downs           = MAX_DOWNS;
297 int                 max_ups             = MAX_UPS;
298 int                 max_backwalks       = MAX_BACKWALKS;
299 int                 max_walks           = MAX_WALKS;
300 int                 max_idles           = MAX_IDLES;
301 int                 max_attack_types    = MAX_ATKS;
302 int                 max_freespecials    = MAX_SPECIALS;
303 int                 max_follows         = MAX_FOLLOWS;
304 int                 max_attacks         = MAX_ATTACKS;
305 int                 max_animations      = MAX_ANIS;
306 
307 // -------dynamic animation indexes-------
308 int                *animdowns           = NULL;
309 int                *animups             = NULL;
310 int                *animbackwalks       = NULL;
311 int                *animwalks           = NULL;
312 int                *animidles           = NULL;
313 int                *animpains           = NULL;
314 int                *animbackpains       = NULL;
315 int                *animdies            = NULL;
316 int                *animbackdies        = NULL;
317 int                *animfalls           = NULL;
318 int                *animbackfalls       = NULL;
319 int                *animrises           = NULL;
320 int                *animriseattacks     = NULL;
321 int                *animblkpains        = NULL;
322 int                *animattacks         = NULL;
323 int                *animfollows         = NULL;
324 int                *animspecials        = NULL;
325 
326 // system default values
327 int                 downs[MAX_DOWNS]        = {ANI_DOWN};
328 int                 ups[MAX_UPS]            = {ANI_UP};
329 int                 backwalks[MAX_BACKWALKS] = {ANI_BACKWALK};
330 int                 walks[MAX_WALKS]        = {ANI_WALK};
331 int                 idles[MAX_IDLES]        = {ANI_IDLE};
332 
333 int                 falls[MAX_ATKS] =
334 {
335     ANI_FALL,  ANI_FALL2, ANI_FALL3, ANI_FALL4,
336     ANI_FALL,  ANI_BURN,  ANI_FALL,  ANI_SHOCK,
337     ANI_FALL,  ANI_FALL5, ANI_FALL6, ANI_FALL7,
338     ANI_FALL8, ANI_FALL9, ANI_FALL10, ANI_FALL,
339     ANI_FALL, ANI_FALL, ANI_FALL, ANI_FALL,
340 };
341 
342 int                 backfalls[MAX_ATKS] =
343 {
344     ANI_BACKFALL,  ANI_BACKFALL2, ANI_BACKFALL3, ANI_BACKFALL4,
345     ANI_BACKFALL,  ANI_BURN,  ANI_BACKFALL,  ANI_SHOCK,
346     ANI_BACKFALL,  ANI_BACKFALL5, ANI_BACKFALL6, ANI_BACKFALL7,
347     ANI_BACKFALL8, ANI_BACKFALL9, ANI_BACKFALL10, ANI_BACKFALL,
348     ANI_BACKFALL, ANI_BACKFALL, ANI_BACKFALL, ANI_BACKFALL,
349 };
350 
351 int                 rises[MAX_ATKS] =
352 {
353     ANI_RISE,  ANI_RISE2, ANI_RISE3, ANI_RISE4,
354     ANI_RISE,  ANI_RISEB,  ANI_RISE,  ANI_RISES,
355     ANI_RISE,  ANI_RISE5, ANI_RISE6, ANI_RISE7,
356     ANI_RISE8, ANI_RISE9, ANI_RISE10, ANI_RISE,
357     ANI_RISE, ANI_RISE, ANI_RISE, ANI_RISE,
358 };
359 
360 int                 riseattacks[MAX_ATKS] =
361 {
362     ANI_RISEATTACK,  ANI_RISEATTACK2, ANI_RISEATTACK3, ANI_RISEATTACK4,
363     ANI_RISEATTACK,  ANI_RISEATTACKB,  ANI_RISEATTACK,  ANI_RISEATTACKS,
364     ANI_RISEATTACK,  ANI_RISEATTACK5, ANI_RISEATTACK6, ANI_RISEATTACK7,
365     ANI_RISEATTACK8, ANI_RISEATTACK9, ANI_RISEATTACK10, ANI_RISEATTACK,
366     ANI_RISEATTACK, ANI_RISEATTACK, ANI_RISEATTACK, ANI_RISEATTACK,
367 };
368 
369 int                 pains[MAX_ATKS] =
370 {
371     ANI_PAIN,  ANI_PAIN2,    ANI_PAIN3, ANI_PAIN4,
372     ANI_PAIN,  ANI_BURNPAIN, ANI_PAIN,  ANI_SHOCKPAIN,
373     ANI_PAIN,  ANI_PAIN5,    ANI_PAIN6, ANI_PAIN7,
374     ANI_PAIN8, ANI_PAIN9,    ANI_PAIN10, ANI_PAIN,
375     ANI_PAIN, ANI_PAIN, ANI_PAIN, ANI_PAIN,
376 };
377 
378 int                 backpains[MAX_ATKS] =
379 {
380     ANI_BACKPAIN,  ANI_BACKPAIN2,    ANI_BACKPAIN3, ANI_BACKPAIN4,
381     ANI_BACKPAIN,  ANI_BURNPAIN, ANI_BACKPAIN,  ANI_SHOCKPAIN,
382     ANI_BACKPAIN,  ANI_BACKPAIN5,    ANI_BACKPAIN6, ANI_BACKPAIN7,
383     ANI_BACKPAIN8, ANI_BACKPAIN9,    ANI_BACKPAIN10, ANI_BACKPAIN,
384     ANI_BACKPAIN, ANI_BACKPAIN, ANI_BACKPAIN, ANI_BACKPAIN,
385 };
386 
387 int                 deaths[MAX_ATKS] =
388 {
389     ANI_DIE,   ANI_DIE2,     ANI_DIE3,  ANI_DIE4,
390     ANI_DIE,   ANI_BURNDIE,  ANI_DIE,   ANI_SHOCKDIE,
391     ANI_DIE,   ANI_DIE5,     ANI_DIE6,  ANI_DIE7,
392     ANI_DIE8,  ANI_DIE9,     ANI_DIE10, ANI_DIE,
393     ANI_DIE, ANI_DIE, ANI_DIE, ANI_DIE,
394 };
395 
396 int                 backdeaths[MAX_ATKS] =
397 {
398     ANI_BACKDIE,   ANI_BACKDIE2,     ANI_BACKDIE3,  ANI_BACKDIE4,
399     ANI_BACKDIE,   ANI_BURNDIE,  ANI_BACKDIE,   ANI_SHOCKDIE,
400     ANI_BACKDIE,   ANI_BACKDIE5,     ANI_BACKDIE6,  ANI_BACKDIE7,
401     ANI_BACKDIE8,  ANI_BACKDIE9,     ANI_BACKDIE10, ANI_BACKDIE,
402     ANI_BACKDIE, ANI_BACKDIE, ANI_BACKDIE, ANI_BACKDIE,
403 };
404 
405 int                 blkpains[MAX_ATKS] =
406 {
407     ANI_BLOCKPAIN,  ANI_BLOCKPAIN2,    ANI_BLOCKPAIN3, ANI_BLOCKPAIN4,
408     ANI_BLOCKPAIN,  ANI_BLOCKPAINB, ANI_BLOCKPAIN,  ANI_BLOCKPAINS,
409     ANI_BLOCKPAIN,  ANI_BLOCKPAIN5,    ANI_BLOCKPAIN6, ANI_BLOCKPAIN7,
410     ANI_BLOCKPAIN8, ANI_BLOCKPAIN9,    ANI_BLOCKPAIN10, ANI_BLOCKPAIN,
411     ANI_BLOCKPAIN, ANI_BLOCKPAIN, ANI_BLOCKPAIN, ANI_BLOCKPAIN,
412 };
413 
414 int                 normal_attacks[MAX_ATTACKS] =
415 {
416     ANI_ATTACK1, ANI_ATTACK2, ANI_ATTACK3, ANI_ATTACK4
417 };
418 
419 int                 grab_attacks[5][2] =
420 {
421     {ANI_GRABATTACK, ANI_GRABATTACK2},
422     {ANI_GRABFORWARD, ANI_GRABFORWARD2},
423     {ANI_GRABUP, ANI_GRABUP2},
424     {ANI_GRABDOWN, ANI_GRABDOWN2},
425     {ANI_GRABBACKWARD, ANI_GRABBACKWARD2}
426 };
427 
428 int                 freespecials[MAX_SPECIALS] =
429 {
430     ANI_FREESPECIAL,   ANI_FREESPECIAL2,  ANI_FREESPECIAL3,
431     ANI_FREESPECIAL4,  ANI_FREESPECIAL5,  ANI_FREESPECIAL6,
432     ANI_FREESPECIAL7,  ANI_FREESPECIAL8
433 };
434 
435 int                 follows[MAX_FOLLOWS] =
436 {
437     ANI_FOLLOW1, ANI_FOLLOW2, ANI_FOLLOW3, ANI_FOLLOW4
438 };
439 
440 // background cache to speed up in-game menus
441 #ifdef CACHE_BACKGROUNDS
442 s_screen           *bg_cache[MAX_CACHED_BACKGROUNDS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
443 unsigned char		bg_palette_cache[MAX_CACHED_BACKGROUNDS][MAX_PAL_SIZE];
444 #endif
445 
446 s_debug_xy_msg      debug_xy_msg;
447 int                 cameratype          = 0;
448 int					defaultmaxplayers	= 2;
449 
450 u32                 go_time             = 0;
451 u32                 time                = 0;
452 u32                 newtime             = 0;
453 s_slow_motion       slowmotion          = { .toggle     = SLOW_MOTION_OFF,
454                                             .counter    = 0,
455                                             .duration   = 2};
456 int                 disablelog          = 0;
457 int                 currentspawnplayer  = 0;
458 int					ent_list_size		= 0;
459 int                 PLAYER_MIN_Z        = 160;
460 int                 PLAYER_MAX_Z        = 232;
461 int                 BGHEIGHT            = 160;
462 int                 MAX_WALL_HEIGHT     = 1000;					// Max wall height that an entity can be spawned on
463 int                 saveslot            = 0;
464 int                 current_palette     = 0;
465 int                 fade                = 24;
466 int                 credits             = 0;
467 int                 gosound             = 0;					// Used to prevent go sound playing too frequently,
468 int                 musicoverlap        = 0;
469 int                 colorbars           = 0;
470 int                 current_spawn       = 0;
471 int                 level_completed     = 0;
472 int                 nojoin              = 0;					// dont allow new hero to join in, use "Please Wait" instead of "Select Hero"
473 int                 groupmin            = 0;
474 int					groupmax            = 0;
475 int                 selectScreen        = 0;					// Flag to determine if at select screen (used for setting animations)
476 int					titleScreen			= 0;
477 int					menuScreen			= 0;
478 int					enginecreditsScreen		= 0;								// CRxTRDude - Flag to determine if the credits for the engine is shown.
479 int					hallOfFame			= 0;
480 int					optionsMenu			= 0;
481 int					newgameMenu			= 0;
482 int					loadgameMenu		= 0;
483 int					controloptionsMenu	= 0;
484 int					videooptionsMenu	= 0;
485 int					soundoptionsMenu	= 0;
486 int					systemoptionsMenu	= 0;
487 int					startgameMenu		= 0;
488 int					gameOver			= 0;
489 int					showComplete		= 0;
490 char				*currentScene		= NULL;
491 int                 tospeedup           = 0;          			// If set will speed the level back up after a boss hits the ground
492 int                 reached[4]          = {0, 0, 0, 0};			// Used with TYPE_ENDLEVEL to determine which players have reached the point //4player
493 int                 noslowfx			= 0;           			// Flag to determine if sound speed when hitting opponent slows or not
494 int                 equalairpause 		= 0;         			// If set to 1, there will be no extra pausetime for players who hit multiple enemies in midair
495 int                 hiscorebg			= 0;					// If set to 1, will look for a background image to display at the highscore screen
496 int                 completebg			= 0;           			// If set to 1, will look for a background image to display at the showcomplete screen
497 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
498 int					loadingmusic        = 0;
499 int                 unlockbg            = 0;         			// If set to 1, will look for a different background image after defeating the game
500 int                 pause               = 0;
501 int                 goto_mainmenu_flag  = 0;
502 int					nofadeout			= 0;
503 int					nosave				= 0;
504 int                 nopause             = 0;                    // OX. If set to 1 , pausing the game will be disabled.
505 int                 noscreenshot        = 0;                    // OX. If set to 1 , taking screenshots is disabled.
506 int                 endgame             = 0;
507 int                 forcecheatsoff      = 0;
508 int                 nodebugoptions      = 0;
509 int                 cheats              = 0;
510 int                 livescheat          = 0;
511 int                 keyscriptrate       = 0;
512 int                 creditscheat        = 0;
513 int                 healthcheat         = 0;
514 int                 showtimeover        = 0;
515 int                 sameplayer          = 0;            		// 7-1-2005  flag to determine if players can use the same character
516 int                 PLAYER_LIVES        = 3;					// 7-1-2005  default setting for Lives
517 int                 CONTINUES           = 5;					// 7-1-2005  default setting for continues
518 int                 colourselect		= 0;					// 6-2-2005 Colour select is optional
519 int                 autoland			= 0;					// Default set to no autoland and landing is valid with u j combo
520 int                 ajspecial			= 0;					// Flag to determine if holding down attack and pressing jump executes special
521 int                 nolost				= 0;					// variable to control if drop weapon when grab a enemy by tails
522 int                 nocost				= 0;					// If set, special will not cost life unless an enemy is hit
523 int                 mpstrict			= 0;					// If current system will check all animation's energy cost when set new animations
524 int                 magic_type			= 0;					// use for restore mp by time by tails
525 entity             *textbox				= NULL;
526 entity             *smartbomber			= NULL;
527 entity				*stalker				= NULL;					// an enemy (usually) tries to go behind the player
528 entity				*firstplayer			= NULL;
529 int					stalking			= 0;
530 int					nextplan			= 0;
531 int                 plife[4][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player lifebar
532 int                 plifeX[4][3]        = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable player lifebar 'x'
533 int                 plifeN[4][3]        = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable player lifebar number of lives
534 int                 picon[4][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player icon
535 int                 piconw[4][2]        = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player weapon icons
536 int                 mpicon[4][2]        = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable magicbar player icon
537 int                 pnameJ[4][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
538 int                 pscore[4][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
539 int                 pshoot[4][3]        = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable player shootnum
540 int                 prush[4][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
541 int                 psmenu[4][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
542 int                 mpcolourtable[11]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
543 int                 hpcolourtable[11]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
544 int                 ldcolourtable[11]   = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
545 char                musicname[128]      = {""};
546 char                currentmusic[128]    = {""};
547 float               musicfade[2]        = {0, 0};
548 int                 musicloop           = 0;
549 u32                 musicoffset         = 0;
550 int					alwaysupdate		= 0; //execute update/updated scripts whenever it has a chance
551 
552 s_barstatus loadingbarstatus =
553 {
554     .size           = { .x = 0,
555                         .y = 10},
556     .offset         = { .x = 0,
557                         .y = 0},
558     .type           = PERCENTAGEBAR,
559     .orientation    = HORIZONTALBAR,
560     .noborder       = 0,
561     .direction      = BARSTATUS_DIR_NORMAL,
562     .barlayer       = 0,
563     .backlayer      = 0,
564     .borderlayer    = 0,
565     .shadowlayer    = 0,
566     .colourtable    = &ldcolourtable
567 };
568 
569 s_barstatus lbarstatus, olbarstatus =
570 {
571     .size           = { .x = 0,
572                         .y = 0},
573     .offset         = { .x = 0,
574                         .y = 0},
575     .type           = VALUEBAR,
576     .orientation    = HORIZONTALBAR,
577     .noborder       = 0,
578     .direction      = BARSTATUS_DIR_NORMAL,
579     .barlayer       = 0,
580     .backlayer      = 0,
581     .borderlayer    = 0,
582     .shadowlayer    = 0,
583     .colourtable    = &hpcolourtable
584 };
585 
586 s_barstatus mpbarstatus =
587 {
588     .size           = { .x = 0,
589                         .y = 0},
590     .offset         = { .x = 0,
591                         .y = 0},
592     .type           = VALUEBAR,
593     .orientation    = HORIZONTALBAR,
594     .noborder       = 0,
595     .direction      = BARSTATUS_DIR_NORMAL,
596     .barlayer       = 0,
597     .backlayer      = 0,
598     .borderlayer    = 0,
599     .shadowlayer    = 0,
600     .colourtable    = &mpcolourtable
601 };
602 
603 int                 timeloc[6]			= {0, 0, 0, 0, 0, -1};		// Used for customizable timeclock location/size
604 int                 timeicon			= -1;
605 int                 timeicon_offsets[2] = {0, 0};
606 char                timeicon_path[128]  = {""};
607 int                 bgicon   			= -1;
608 int                 bgicon_offsets[3]	= {0, 0, 0};
609 char                bgicon_path[128]    = {""};
610 int                 olicon    			= -1;
611 int                 olicon_offsets[3]	= {0, 0, 0};
612 char                olicon_path[128]    = {""};
613 int                 elife[4][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable enemy lifebar
614 int                 ename[4][3]         = {{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}}; // Used for customizable enemy name
615 int                 eicon[4][2]         = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable enemy icon
616 int                 scomplete[6]		= {0, 0, 0, 0, 0, 0};		// Used for customizable Stage # Complete
617 int                 cbonus[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable clear bonus
618 int                 lbonus[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable life bonus
619 int                 rbonus[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable rush bonus
620 int                 tscore[10]          = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Used for customizable total score
621 int                 scbonuses[4]        = {10000, 1000, 100, 0};//Stage complete bonus multipliers
622 int                 showrushbonus       = 0;
623 int                 noshare				= 0;					// Used for when you want to keep p1 & p2 credits separate
624 int                 nodropen			= 0;					// Drop or not when spawning is now a modder option
625 int					nodropspawn			= 0;					// don't spawn from the sky if the modder doesn't set it
626 int                 gfx_x_offset		= 0;                    //2011_04_03, DC: Enable X offset adjustment by modders.
627 int                 gfx_y_offset		= 0;
628 int                 gfx_y_offset_adj    = 0;                    //2011_04_03, DC: Enable Y offset adjustment by modders.
629 
630 // 2011/10/22 UT: temporary solution for custom viewport
631 int					viewportx			= 0;
632 int					viewporty			= 0;
633 int					viewportw			= 0;
634 int					viewporth			= 0;
635 
636 
637 int                 timeleft			= 0;
638 int                 oldtime             = 0;                    // One second back from time left.
639 int                 holez				= 0;					// Used for setting spawn points
640 int                 allow_secret_chars	= 0;
641 unsigned int        lifescore			= 50000;				// Number of points needed to earn a 1-up
642 unsigned int        credscore			= 0;					// Number of points needed to earn a credit
643 int                 mpblock				= 0;					// Take chip damage from health or MP first?
644 int                 blockratio			= 0;					// Take half-damage while blocking?
645 int                 nochipdeath			= 0;					// Prevents entities from dying due to chip damage (damage while blocking)
646 int                 noaircancel         = 0;					// Now, you can make jumping attacks uncancellable!
647 int                 nomaxrushreset[5]   = {0, 0, 0, 0, 0};
648 int			        mpbartext[4]		= { -1, 0, 0, 0};			// Array for adjusting MP status text (font, Xpos, Ypos, Display type).
649 int			        lbartext[4]			= { -1, 0, 0, 0};			// Array for adjusting HP status text (font, Xpos, Ypos, Display type).
650 int                 pmp[4][2]			= {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Used for customizable player mpbar
651 int                 spdirection[4]		= {1, 0, 1, 0};			// Used for Select Player Direction for select player screen
652 int                 bonus				= 0;					// Used for unlocking Bonus difficulties
653 int                 versusdamage		= 2;					// Used for setting mode. (ability to hit other players)
654 int                 z_coords[3]			= {0, 0, 0};				// Used for setting customizable walkable area
655 int                 rush[6]				= {0, 2, 3, 3, 3, 3};
656 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)
657 int                 color_black			= 0;
658 int                 color_red			= 0;
659 int                 color_orange		= 0;
660 int                 color_yellow		= 0;
661 int                 color_white			= 0;
662 int                 color_blue			= 0;
663 int                 color_green			= 0;
664 int                 color_pink			= 0;
665 int                 color_purple		= 0;
666 int                 color_magic			= 0;
667 int                 color_magic2		= 0;
668 int                 lifebarfgalpha      = 0;
669 int                 lifebarbgalpha      = 2;
670 int                 shadowsprites[6]	= { -1, -1, -1, -1, -1, -1};
671 int                 gosprite			= -1;
672 int                 golsprite			= -1;
673 int                 holesprite			= -1;
674 int                 videoMode			= 0;
675 int                 scoreformat			= 0;					// If set fill score values with 6 Zeros
676 
677 // Funny neon lights
678 unsigned char       neontable[MAX_PAL_SIZE];
679 unsigned int        neon_time			= 0;
680 
681 int                 panel_width			= 0;
682 int                 panel_height		= 0;
683 int                 frontpanels_loaded	= 0;
684 
685 unsigned int        sprites_loaded		= 0;
686 unsigned int        anims_loaded		= 0;
687 
688 unsigned int        models_loaded		= 0;
689 unsigned int        models_cached		= 0;
690 
691 entity            **ent_list;
692 entity            **ent_stack; //temporary list, reference only
693 int					ent_stack_size = 0;
694 entity             *self;
695 int                 ent_count			= 0;					// log count of entites
696 int                 ent_max				= 0;
697 
698 s_player            player[4];
699 u32                 bothkeys, bothnewkeys;
700 
701 s_playercontrols    playercontrols1;
702 s_playercontrols    playercontrols2;
703 s_playercontrols    playercontrols3;
704 s_playercontrols    playercontrols4;
705 s_playercontrols   *playercontrolpointers[] = {&playercontrols1, &playercontrols2, &playercontrols3, &playercontrols4};
706 
707 //global script
708 Script level_script;    //execute when level start
709 Script endlevel_script; //execute when level finished
710 Script update_script;   //execute when ingame update
711 Script updated_script;  //execute when ingame update finished
712 Script loading_script;	// in loading screen
713 Script key_script_all;  //keyscript for all players
714 Script timetick_script; //time tick script.
715 
716 //player script
717 Script score_script[4];     //execute when add score, 4 players
718 Script key_script[4];       //key listeners, lol
719 Script join_script[4];      //player join scripts
720 Script respawn_script[4];   //player respawn scripts
721 Script pdie_script[4];      //player death scripts
722 
723 extern Script *pcurrentscript;//used by local script functions
724 //-------------------------methods-------------------------------
725 
setDrawMethod(s_anim * a,ptrdiff_t index,s_drawmethod * m)726 void setDrawMethod(s_anim *a, ptrdiff_t index, s_drawmethod *m)
727 {
728     assert(index >= 0);
729     assert(a != NULL);
730     assert(m != NULL);
731     assert(index < a->numframes);
732     a->drawmethods[index] = m;
733 }
734 
getDrawMethod(s_anim * a,ptrdiff_t index)735 s_drawmethod *getDrawMethod(s_anim *a, ptrdiff_t index)
736 {
737     assert(index >= 0);
738     assert(a != NULL);
739     assert(index < a->numframes);
740     return a->drawmethods[index];
741 }
742 
isLoadingScreenTypeBg(e_loadingScreenType what)743 int isLoadingScreenTypeBg(e_loadingScreenType what)
744 {
745     return (what & LS_TYPE_BACKGROUND) == LS_TYPE_BACKGROUND;
746 }
747 
isLoadingScreenTypeBar(e_loadingScreenType what)748 int isLoadingScreenTypeBar(e_loadingScreenType what)
749 {
750     return (what & LS_TYPE_BAR) == LS_TYPE_BAR;
751 }
752 
fill_s_loadingbar(s_loadingbar * s,e_loadingScreenType set,int bx,int by,int bsize,int tx,int ty,int tf,int ms)753 char *fill_s_loadingbar(s_loadingbar *s, e_loadingScreenType set, int bx, int by, int bsize, int tx, int ty, int tf, int ms)
754 {
755     switch (set)
756     {
757         case LS_TYPE_BOTH:
758             s->set = (LS_TYPE_BACKGROUND | LS_TYPE_BAR);
759             break;
760         case LS_TYPE_BACKGROUND:
761             s->set = LS_TYPE_BACKGROUND;
762             break;
763         case LS_TYPE_BAR:
764             s->set = LS_TYPE_BAR;
765             break;
766         case LS_TYPE_NONE:
767             s->set = LS_TYPE_NONE;
768             break;
769         default:
770             s->set = LS_TYPE_NONE;
771             printf("invalid loadingbg type %d!\n", set);
772     }
773     s->tf = tf;
774     s->bar_position.x = bx;
775     s->bar_position.y = by;
776     s->bsize = bsize;
777     s->text_position.x = tx;
778     s->text_position.y = ty;
779     s->refreshMs = (ms ? ms : 100);
780     return NULL;
781 }
782 
783 
buffer_file(char * filename,char ** pbuffer,size_t * psize)784 static int buffer_file(char *filename, char **pbuffer, size_t *psize)
785 {
786     FILE *handle;
787     *psize = 0;
788     *pbuffer = NULL;
789     // Read file
790 #ifdef VERBOSE
791     printf("file requested: %s.\n", filename);
792 #endif
793 
794     if(!(handle = fopen(filename, "rb")) )
795     {
796 #ifdef VERBOSE
797         printf("couldnt get handle!\n");
798 #endif
799         return 0;
800     }
801     fseek(handle, 0, SEEK_END);
802     *psize = ftell(handle);
803     fseek(handle, 0, SEEK_SET);
804 
805     *pbuffer = (char *)malloc(*psize + 1);
806     if(*pbuffer == NULL)
807     {
808         *psize = 0;
809         fclose(handle);
810         shutdown(1, "Can't create buffer for file '%s'", filename);
811         return 0;
812     }
813     if(fread(*pbuffer, 1, *psize, handle) != *psize)
814     {
815         if(*pbuffer != NULL)
816         {
817             free(*pbuffer);
818             *pbuffer = NULL;
819             *psize = 0;
820         }
821         fclose(handle);
822         shutdown(1, "Can't read from file '%s'", filename);
823         return 0;
824     }
825     (*pbuffer)[*psize] = 0;        // Terminate string (important!)
826     fclose(handle);
827     return 1;
828 }
829 
830 
831 // returns: 1 - succeeded 0 - failed
buffer_pakfile(char * filename,char ** pbuffer,size_t * psize)832 int buffer_pakfile(char *filename, char **pbuffer, size_t *psize)
833 {
834     int handle;
835     *psize = 0;
836     *pbuffer = NULL;
837 
838     if(buffer_file(filename, pbuffer, psize) == 1)
839     {
840         return 1;
841     }
842 
843     // Read file
844 #ifdef VERBOSE
845     printf("pakfile requested: %s.\n", filename); //ASDF
846 #endif
847 
848     if((handle = openpackfile(filename, packfile)) < 0)
849     {
850 #ifdef VERBOSE
851         printf("couldnt get handle!\n");
852 #endif
853         return 0;
854     }
855     *psize = seekpackfile(handle, 0, SEEK_END);
856     seekpackfile(handle, 0, SEEK_SET);
857 
858     *pbuffer = (char *)malloc(*psize + 1);
859     if(*pbuffer == NULL)
860     {
861         *psize = 0;
862         closepackfile(handle);
863         shutdown(1, "Can't create buffer for packfile '%s'", filename);
864         return 0;
865     }
866     if(readpackfile(handle, *pbuffer, *psize) != *psize)
867     {
868         if(*pbuffer != NULL)
869         {
870             free(*pbuffer);
871             *pbuffer = NULL;
872             *psize = 0;
873         }
874         closepackfile(handle);
875         shutdown(1, "Can't read from packfile '%s'", filename);
876         return 0;
877     }
878     (*pbuffer)[*psize] = 0;        // Terminate string (important!)
879     closepackfile(handle);
880     return 1;
881 }
882 
buffer_append(char ** buffer,const char * str,size_t n,size_t * bufferlen,size_t * len)883 int buffer_append(char **buffer, const char *str, size_t n, size_t *bufferlen, size_t *len)
884 {
885     size_t appendlen = strlen(str);
886     if(appendlen > n)
887     {
888         appendlen = n;
889     }
890     if(appendlen + *len + 1 > *bufferlen)
891     {
892         //printf("*Debug* reallocating buffer...\n");
893         *buffer = realloc(*buffer, *bufferlen = appendlen + *len + 1024);
894         if(*buffer == NULL)
895         {
896             shutdown(1, "Unalbe to resize buffer.\n");
897         }
898     }
899     strncpy(*buffer + *len, str, appendlen);
900     *len = *len + appendlen;
901     (*buffer)[*len] = 0;
902     return *len;
903 }
904 
handle_txt_include(char * command,ArgList * arglist,char ** fn,char * namebuf,char ** buf,ptrdiff_t * pos,size_t * len)905 int handle_txt_include(char *command, ArgList *arglist, char **fn, char *namebuf, char **buf, ptrdiff_t *pos, size_t *len)
906 {
907     char *incfile, *filename = *fn, *buf2, *endstr = "\r\n@end";
908     size_t size, t;
909     if(stricmp(command, "@include") == 0)
910     {
911         incfile = GET_ARGP(1);
912         buffer_pakfile(incfile, &buf2, &size) ;
913         if(buf2)
914         {
915             *buf = realloc(*buf, *len + size + strlen(incfile) + strlen(filename) + 100); //leave enough memory for jump command
916             if(*buf == NULL)
917             {
918                 shutdown(1, "Unalbe to resize buffer. (handle_txt_include)\n");
919                 free(buf2);
920                 return 0;
921             }
922             sprintf((*buf) + *len - 1, "%s\r\n@filename %s\r\n", endstr, incfile);
923             strcat((*buf) + *len, buf2);
924             t = strlen(*buf);
925             sprintf((*buf) + t, "\r\n@filename %s\r\n@jump %d\r\n", filename, (int)(*pos));
926             (*buf)[*pos] = '#';
927             *pos = *len + strlen(endstr); //continue from the new file position
928             *len = strlen(*buf);
929             free(buf2);
930             //printf(*buf);
931             return 1;
932         }
933         shutdown(1, "Can't find file '%s' to include.\n", incfile);
934     }
935     else if(stricmp(command, "@jump") == 0)
936     {
937         *pos = GET_INT_ARGP(1);
938         return 2;
939     }
940     else if(stricmp(command, "@end") == 0)
941     {
942         *pos = *len;
943         return 3;
944     }
945     else if(stricmp(command, "@filename") == 0)
946     {
947         strcpy(namebuf, GET_ARGP(1));
948         *fn = namebuf;
949         return 4;
950     }
951     return 0;
952 }
953 
954 //this method is used by script engine, we move it here
955 // it will get a system property, put it in the ScriptVariant
956 // if failed return 0, otherwise return 1
getsyspropertybyindex(ScriptVariant * var,int index)957 int getsyspropertybyindex(ScriptVariant *var, int index)
958 {
959     if(!var)
960     {
961         return 0;
962     }
963 
964     switch(index)
965     {
966     case _sv_count_enemies:
967         ScriptVariant_ChangeType(var, VT_INTEGER);
968         var->lVal = count_ents(TYPE_ENEMY);
969         break;
970     case _sv_count_players:
971         ScriptVariant_ChangeType(var, VT_INTEGER);
972         var->lVal = count_ents(TYPE_PLAYER);
973         break;
974     case _sv_count_npcs:
975         ScriptVariant_ChangeType(var, VT_INTEGER);
976         var->lVal = count_ents(TYPE_NPC);
977         break;
978     case _sv_count_entities:
979         ScriptVariant_ChangeType(var, VT_INTEGER);
980         var->lVal = ent_count;
981         break;
982     case _sv_ent_max:
983         ScriptVariant_ChangeType(var, VT_INTEGER);
984         var->lVal = ent_max;
985         break;
986     case _sv_in_level:
987         ScriptVariant_ChangeType(var, VT_INTEGER);
988         var->lVal = (level != NULL);
989         break;
990     case _sv_in_gameoverscreen:
991         ScriptVariant_ChangeType(var, VT_INTEGER);
992         var->lVal = gameOver;
993         break;
994     case _sv_in_menuscreen:
995         ScriptVariant_ChangeType(var, VT_INTEGER);
996         if(selectScreen || titleScreen || hallOfFame || gameOver || showComplete || currentScene || level || enginecreditsScreen)
997         {
998             var->lVal = 0;
999         }
1000         else
1001         {
1002             var->lVal = menuScreen;
1003         }
1004         break;
1005     case _sv_in_enginecreditsscreen:
1006     		ScriptVariant_ChangeType(var, VT_INTEGER);
1007     		var->lVal = enginecreditsScreen;
1008     		break;
1009     case _sv_in_options:
1010         ScriptVariant_ChangeType(var, VT_INTEGER);
1011         var->lVal = optionsMenu;
1012         break;
1013     case _sv_in_system_options:
1014         ScriptVariant_ChangeType(var, VT_INTEGER);
1015         var->lVal = systemoptionsMenu;
1016         break;
1017     case _sv_in_cheat_options:
1018         ScriptVariant_ChangeType(var, VT_INTEGER);
1019         var->lVal = (optionsMenu && is_cheat_actived()) ? 1:0;
1020         break;
1021     case _sv_cheats:
1022         ScriptVariant_ChangeType(var, VT_INTEGER);
1023         var->lVal = is_cheat_actived();
1024         break;
1025     case _sv_in_control_options:
1026         ScriptVariant_ChangeType(var, VT_INTEGER);
1027         var->lVal = controloptionsMenu;
1028         break;
1029     case _sv_in_sound_options:
1030         ScriptVariant_ChangeType(var, VT_INTEGER);
1031         var->lVal = soundoptionsMenu;
1032         break;
1033     case _sv_in_video_options:
1034         ScriptVariant_ChangeType(var, VT_INTEGER);
1035         var->lVal = videooptionsMenu;
1036         break;
1037     case _sv_in_start_game:
1038         ScriptVariant_ChangeType(var, VT_INTEGER);
1039         var->lVal = startgameMenu;
1040         break;
1041     case _sv_in_new_game:
1042         ScriptVariant_ChangeType(var, VT_INTEGER);
1043         var->lVal = newgameMenu;
1044         break;
1045     case _sv_in_load_game:
1046         ScriptVariant_ChangeType(var, VT_INTEGER);
1047         var->lVal = loadgameMenu;
1048         break;
1049     case _sv_in_showcomplete:
1050         ScriptVariant_ChangeType(var, VT_INTEGER);
1051         var->lVal = showComplete;
1052         break;
1053     case _sv_in_titlescreen:
1054         ScriptVariant_ChangeType(var, VT_INTEGER);
1055         var->lVal = titleScreen;
1056         break;
1057     case _sv_in_halloffamescreen:
1058         ScriptVariant_ChangeType(var, VT_INTEGER);
1059         var->lVal = (hallOfFame);
1060         break;
1061     case _sv_sample_play_id:
1062         ScriptVariant_ChangeType(var, VT_INTEGER);
1063         var->lVal = sample_play_id;
1064     case _sv_effectvol:
1065         ScriptVariant_ChangeType(var, VT_INTEGER);
1066         var->lVal = savedata.effectvol;
1067         break;
1068     case _sv_elapsed_time:
1069         ScriptVariant_ChangeType(var, VT_INTEGER);
1070         var->lVal = time;
1071         break;
1072     case _sv_game_speed:
1073         if(!level)
1074         {
1075             return 0;
1076         }
1077         ScriptVariant_ChangeType(var, VT_INTEGER);
1078         var->lVal = GAME_SPEED;
1079         break;
1080     case _sv_game_paused:
1081     case _sv_pause:
1082         ScriptVariant_ChangeType(var, VT_INTEGER);
1083         if( !(goto_mainmenu_flag&1) ) var->lVal = (pause);
1084         else var->lVal = 0;
1085         break;
1086     case _sv_porting:
1087     {
1088         e_porting porting;
1089 
1090         #if ANDROID
1091             porting = PORTING_ANDROID;
1092         #elif DARWIN
1093             porting = PORTING_DARWIN;
1094         #elif DC
1095             porting = PORTING_DREAMCAST;
1096         #elif GPX2
1097             porting = PORTING_GPX2;
1098         #elif LINUX
1099             porting = PORTING_LINUX;
1100         #elif OPENDINGUX
1101             porting = PORTING_OPENDINGUX;
1102         #elif PSP
1103             porting = PORTING_PSP;
1104         #elif WII
1105             porting = PORTING_WII;
1106         #elif WIN
1107             porting = PORTING_WINDOWS;
1108         #elif WIZ
1109             porting = PORTING_WIZ;
1110         #elif XBOX
1111             porting = PORTING_XBOX;
1112         #else
1113             porting = PORTING_UNKNOWN;
1114         #endif
1115 
1116         ScriptVariant_ChangeType(var, VT_INTEGER);
1117         var->lVal = porting;
1118         break;
1119     }
1120     case _sv_gfx_x_offset:
1121         if(!level)
1122         {
1123             return 0;
1124         }
1125         ScriptVariant_ChangeType(var, VT_INTEGER);
1126         var->lVal = gfx_x_offset;
1127         break;
1128     case _sv_gfx_y_offset:
1129         if(!level)
1130         {
1131             return 0;
1132         }
1133         ScriptVariant_ChangeType(var, VT_INTEGER);
1134         var->lVal = gfx_y_offset;
1135         break;
1136     case _sv_gfx_y_offset_adj:
1137         if(!level)
1138         {
1139             return 0;
1140         }
1141         ScriptVariant_ChangeType(var, VT_INTEGER);
1142         var->lVal = gfx_y_offset_adj;
1143         break;
1144     case _sv_in_selectscreen:
1145         ScriptVariant_ChangeType(var, VT_INTEGER);
1146         var->lVal = selectScreen;
1147         break;
1148     case _sv_lasthita:
1149     case _sv_lasthity:
1150         ScriptVariant_ChangeType(var, VT_DECIMAL);
1151         var->dblVal = lasthit.position.y;
1152         break;
1153     case _sv_lasthitc:
1154         ScriptVariant_ChangeType(var, VT_INTEGER);
1155         var->lVal = lasthit.confirm;
1156         break;
1157     case _sv_lasthitt:
1158         ScriptVariant_ChangeType(var, VT_INTEGER);
1159 
1160         if(lasthit.attack)
1161         {
1162             var->lVal = lasthit.attack->attack_type;
1163         }
1164 
1165         break;
1166     case _sv_lasthitx:
1167         ScriptVariant_ChangeType(var, VT_DECIMAL);
1168         var->dblVal = lasthit.position.x;
1169         break;
1170     case _sv_lasthitz:
1171         ScriptVariant_ChangeType(var, VT_DECIMAL);
1172         var->dblVal = lasthit.position.z;
1173         break;
1174     case _sv_xpos:
1175         if(!level)
1176         {
1177             return 0;
1178         }
1179         ScriptVariant_ChangeType(var, VT_DECIMAL);
1180         var->dblVal = advancex;
1181         break;
1182     case _sv_ypos:
1183         if(!level)
1184         {
1185             return 0;
1186         }
1187         ScriptVariant_ChangeType(var, VT_DECIMAL);
1188         var->dblVal = advancey;
1189         break;
1190     case _sv_hresolution:
1191         ScriptVariant_ChangeType(var, VT_INTEGER);
1192         var->lVal = videomodes.hRes;
1193         break;
1194     case _sv_vresolution:
1195         ScriptVariant_ChangeType(var, VT_INTEGER);
1196         var->lVal = videomodes.vRes;
1197         break;
1198     case _sv_current_scene:
1199         if(currentScene)
1200         {
1201             ScriptVariant_ChangeType(var, VT_STR);
1202             strcpy(StrCache_Get(var->strVal), currentScene);
1203         }
1204         else
1205         {
1206             ScriptVariant_Clear(var);
1207         }
1208         break;
1209     case _sv_current_set:
1210         ScriptVariant_ChangeType(var, VT_INTEGER);
1211         var->lVal = current_set;
1212         break;
1213     case _sv_current_level:
1214         ScriptVariant_ChangeType(var, VT_INTEGER);
1215         var->lVal = current_level;
1216         break;
1217     case _sv_current_palette:
1218         ScriptVariant_ChangeType(var, VT_INTEGER);
1219         var->lVal = current_palette;
1220         break;
1221     case _sv_current_stage:
1222         ScriptVariant_ChangeType(var, VT_INTEGER);
1223         var->lVal = current_stage;
1224         break;
1225     case _sv_levelwidth:
1226         if(!level)
1227         {
1228             return 0;
1229         }
1230         ScriptVariant_ChangeType(var, VT_INTEGER);
1231         var->lVal = level->width;
1232         break;
1233     case _sv_levelheight:
1234         if(!level)
1235         {
1236             return 0;
1237         }
1238         ScriptVariant_ChangeType(var, VT_INTEGER);
1239         var->lVal = panel_height;
1240         break;
1241     case _sv_branchname:
1242         ScriptVariant_ChangeType(var, VT_STR);
1243         strcpy(StrCache_Get(var->strVal), branch_name);
1244         break;
1245     case _sv_current_branch:
1246         ScriptVariant_ChangeType(var, VT_STR);
1247         if(level != NULL && levelsets && levelsets[current_set].levelorder && levelsets[current_set].levelorder[current_level].branchname)
1248         {
1249             strcpy(StrCache_Get(var->strVal), levelsets[current_set].levelorder[current_level].branchname);
1250         }
1251         else
1252         {
1253             ScriptVariant_Clear(var);
1254         }
1255         break;
1256     case _sv_pakname:
1257         ScriptVariant_ChangeType(var, VT_STR);
1258         getPakName(StrCache_Get(var->strVal), -1);
1259         break;
1260     case _sv_maxsoundchannels:
1261         ScriptVariant_ChangeType(var, VT_INTEGER);
1262         var->lVal = maxchannels();
1263         break;
1264     case _sv_maxentityvars:
1265         ScriptVariant_ChangeType(var, VT_INTEGER);
1266         var->lVal = max_entity_vars;
1267         break;
1268     case _sv_maxindexedvars:
1269         ScriptVariant_ChangeType(var, VT_INTEGER);
1270         var->lVal = max_indexed_vars;
1271         break;
1272     case _sv_maxplayers:
1273         ScriptVariant_ChangeType(var, VT_INTEGER);
1274         var->lVal = levelsets[current_set].maxplayers;
1275         break;
1276     case _sv_maxscriptvars:
1277         ScriptVariant_ChangeType(var, VT_INTEGER);
1278         var->lVal = max_script_vars;
1279         break;
1280     case _sv_models_cached:
1281         ScriptVariant_ChangeType(var, VT_INTEGER);
1282         var->lVal = models_cached;
1283         break;
1284     case _sv_models_loaded:
1285         ScriptVariant_ChangeType(var, VT_INTEGER);
1286         var->lVal = models_loaded;
1287         break;
1288     case _sv_musicvol:
1289         ScriptVariant_ChangeType(var, VT_INTEGER);
1290         var->lVal = savedata.musicvol;
1291         break;
1292     case _sv_nofadeout:
1293         ScriptVariant_ChangeType(var, VT_INTEGER);
1294         var->lVal = nofadeout;
1295         break;
1296     case _sv_nojoin:
1297         ScriptVariant_ChangeType(var, VT_INTEGER);
1298         var->lVal = nojoin;
1299         break;
1300     case _sv_nopause:
1301         ScriptVariant_ChangeType(var, VT_INTEGER);
1302         var->lVal = nopause;
1303         break;
1304     case _sv_nosave:
1305         ScriptVariant_ChangeType(var, VT_INTEGER);
1306         var->lVal = nosave;
1307         break;
1308     case _sv_noscreenshot:
1309         ScriptVariant_ChangeType(var, VT_INTEGER);
1310         var->lVal = noscreenshot;
1311         break;
1312     case _sv_noshowcomplete:
1313         ScriptVariant_ChangeType(var, VT_INTEGER);
1314         var->lVal = levelsets[current_set].noshowcomplete;
1315         break;
1316     case _sv_numbasemaps:
1317         ScriptVariant_ChangeType(var, VT_INTEGER);
1318         var->lVal = level->numbasemaps;
1319         break;
1320     case _sv_numholes:
1321         ScriptVariant_ChangeType(var, VT_INTEGER);
1322         var->lVal = level->numholes;
1323         break;
1324     case _sv_numlayers:
1325         ScriptVariant_ChangeType(var, VT_INTEGER);
1326         var->lVal = level->numlayers;
1327         break;
1328     case _sv_numpalettes:
1329         ScriptVariant_ChangeType(var, VT_INTEGER);
1330         var->lVal = level->numpalettes;
1331         break;
1332     case _sv_numwalls:
1333         ScriptVariant_ChangeType(var, VT_INTEGER);
1334         var->lVal = level->numwalls;
1335         break;
1336     case _sv_pixelformat:
1337         ScriptVariant_ChangeType(var, VT_INTEGER);
1338         var->lVal = pixelformat;
1339         break;
1340     case _sv_player:
1341     case _sv_player1:
1342         ScriptVariant_ChangeType(var, VT_PTR);
1343         var->ptrVal = player;
1344         break;
1345     case _sv_player2:
1346         ScriptVariant_ChangeType(var, VT_PTR);
1347         var->ptrVal = player + 1;
1348         break;
1349     case _sv_player3:
1350         ScriptVariant_ChangeType(var, VT_PTR);
1351         var->ptrVal = player + 2;
1352         break;
1353     case _sv_player4:
1354         ScriptVariant_ChangeType(var, VT_PTR);
1355         var->ptrVal = player + 3;
1356         break;
1357     case _sv_player_max_z:
1358         ScriptVariant_ChangeType(var, VT_INTEGER);
1359         var->lVal = PLAYER_MAX_Z;
1360         break;
1361     case _sv_player_min_z:
1362         ScriptVariant_ChangeType(var, VT_INTEGER);
1363         var->lVal = PLAYER_MIN_Z;
1364         break;
1365     case _sv_lightx:
1366         ScriptVariant_ChangeType(var, VT_INTEGER);
1367         var->lVal = light.x;
1368         break;
1369     case _sv_lightz:
1370         ScriptVariant_ChangeType(var, VT_INTEGER);
1371         var->lVal = light.y;
1372         break;
1373     case _sv_self:
1374         ScriptVariant_ChangeType(var, VT_PTR);
1375         var->ptrVal = self;
1376         break;
1377     case _sv_shadowalpha:
1378         ScriptVariant_ChangeType(var, VT_INTEGER);
1379         var->lVal = shadowalpha;
1380         break;
1381     case _sv_shadowcolor:
1382         ScriptVariant_ChangeType(var, VT_INTEGER);
1383         var->lVal = shadowcolor;
1384         break;
1385     case _sv_shadowopacity:
1386         ScriptVariant_ChangeType(var, VT_INTEGER);
1387         var->lVal = shadowopacity;
1388         break;
1389     case _sv_skiptoset:
1390         ScriptVariant_ChangeType(var, VT_INTEGER);
1391         var->lVal = skiptoset;
1392         break;
1393     case _sv_slowmotion:
1394         ScriptVariant_ChangeType(var, VT_INTEGER);
1395         var->lVal = slowmotion.toggle;
1396         break;
1397     case _sv_slowmotion_duration:
1398         ScriptVariant_ChangeType(var, VT_INTEGER);
1399         var->lVal = slowmotion.duration;
1400         break;
1401     case _sv_soundvol:
1402         ScriptVariant_ChangeType(var, VT_INTEGER);
1403         var->lVal = savedata.soundvol;
1404         break;
1405     case _sv_totalram:
1406         ScriptVariant_ChangeType(var, VT_INTEGER);
1407         var->lVal = getSystemRam(KBYTES);
1408         break;
1409     case _sv_freeram:
1410         ScriptVariant_ChangeType(var, VT_INTEGER);
1411         var->lVal = getFreeRam(KBYTES);
1412         break;
1413     case _sv_usedram:
1414         ScriptVariant_ChangeType(var, VT_INTEGER);
1415         var->lVal = getUsedRam(KBYTES);
1416         break;
1417     case _sv_textbox:
1418         ScriptVariant_ChangeType(var, VT_PTR);
1419         var->ptrVal = textbox;
1420         break;
1421     case _sv_background:
1422         ScriptVariant_ChangeType(var, VT_PTR);
1423         var->ptrVal = background;
1424         break;
1425     case _sv_vscreen:
1426         ScriptVariant_ChangeType(var, VT_PTR);
1427         var->ptrVal = vscreen;
1428         break;
1429     case _sv_viewportx:
1430         ScriptVariant_ChangeType(var, VT_INTEGER);
1431         var->lVal = viewportx;
1432         break;
1433     case _sv_viewporty:
1434         ScriptVariant_ChangeType(var, VT_INTEGER);
1435         var->lVal = viewporty;
1436         break;
1437     case _sv_viewportw:
1438         ScriptVariant_ChangeType(var, VT_INTEGER);
1439         var->lVal = viewportw;
1440         break;
1441     case _sv_viewporth:
1442         ScriptVariant_ChangeType(var, VT_INTEGER);
1443         var->lVal = viewporth;
1444         break;
1445     case _sv_scrollminx:
1446         ScriptVariant_ChangeType(var, VT_DECIMAL);
1447         var->dblVal = scrollminx;
1448         break;
1449     case _sv_scrollmaxx:
1450         ScriptVariant_ChangeType(var, VT_DECIMAL);
1451         var->dblVal = scrollmaxx;
1452         break;
1453     case _sv_scrollminz:
1454         ScriptVariant_ChangeType(var, VT_DECIMAL);
1455         var->dblVal = scrollminz;
1456         break;
1457     case _sv_scrollmaxz:
1458         ScriptVariant_ChangeType(var, VT_DECIMAL);
1459         var->dblVal = scrollmaxz;
1460         break;
1461     case _sv_blockade:
1462         ScriptVariant_ChangeType(var, VT_INTEGER);
1463         var->lVal = blockade;
1464         break;
1465     case _sv_waiting:
1466         ScriptVariant_ChangeType(var, VT_INTEGER);
1467         var->lVal = level ? level->waiting : 0;
1468     case _sv_maxattacktypes:
1469         ScriptVariant_ChangeType(var, VT_INTEGER);
1470         var->lVal = max_attack_types;
1471         break;
1472     case _sv_maxanimations:
1473         ScriptVariant_ChangeType(var, VT_INTEGER);
1474         var->lVal = max_animations;
1475         break;
1476     case _sv_ticks:
1477         ScriptVariant_ChangeType(var, VT_INTEGER);
1478         var->lVal = timer_gettick();
1479         break;
1480     case _sv_nogameover:
1481         ScriptVariant_ChangeType(var, VT_INTEGER);
1482         var->lVal = levelsets[current_set].noshowgameover; // or s_set_entry *set = levelsets + current_set;
1483         break;
1484     case _sv_nohof:
1485         ScriptVariant_ChangeType(var, VT_INTEGER);
1486         var->lVal = levelsets[current_set].noshowhof;
1487         break;
1488     case _sv_fps:
1489         ScriptVariant_ChangeType(var, VT_INTEGER);
1490         var->lVal = getFPS();
1491         break;
1492     default:
1493         // We use indices now, but players/modders don't need to be exposed
1494         // to that implementation detail, so we write "name" and not "index".
1495         printf("Unknown system property name.\n");
1496         return 0;
1497     }
1498     return 1;
1499 }
1500 
1501 // change a system variant, used by script
changesyspropertybyindex(int index,ScriptVariant * value)1502 int changesyspropertybyindex(int index, ScriptVariant *value)
1503 {
1504     //char* tempstr = NULL;
1505     LONG ltemp;
1506     //DOUBLE dbltemp;
1507 
1508     switch(index)
1509     {
1510     case _sv_elapsed_time:
1511         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1512         {
1513             time = (int)ltemp;
1514         }
1515         break;
1516     case _sv_current_stage:
1517         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1518         {
1519             current_stage = (int)ltemp;
1520         }
1521         break;
1522     case _sv_current_set:
1523         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1524         {
1525             current_set = (int)ltemp;
1526         }
1527         break;
1528     case _sv_current_level:
1529         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1530         {
1531             current_level = (int)ltemp;
1532         }
1533         break;
1534     case _sv_gfx_x_offset:
1535         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1536         {
1537             gfx_x_offset = (int)ltemp;
1538         }
1539         break;
1540     case _sv_gfx_y_offset:
1541         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1542         {
1543             gfx_y_offset = (int)ltemp;
1544         }
1545         break;
1546     case _sv_gfx_y_offset_adj:
1547         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1548         {
1549             gfx_y_offset_adj = (int)ltemp;
1550         }
1551         break;
1552     case _sv_levelpos:
1553         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1554         {
1555             level->pos = (int)ltemp;
1556         }
1557         break;
1558     case _sv_xpos:
1559         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1560         {
1561             advancex = (float)ltemp;
1562         }
1563         break;
1564     case _sv_ypos:
1565         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1566         {
1567             advancey = (float)ltemp;
1568         }
1569         break;
1570     case _sv_scrollminz:
1571         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1572         {
1573             scrollminz = (float)ltemp;
1574         }
1575         break;
1576     case _sv_scrollmaxz:
1577         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1578         {
1579             scrollmaxz = (float)ltemp;
1580         }
1581         break;
1582     case _sv_scrollminx:
1583         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1584         {
1585             scrollminx = (float)ltemp;
1586         }
1587         break;
1588     case _sv_scrollmaxx:
1589         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1590         {
1591             scrollmaxx = (float)ltemp;
1592         }
1593         break;
1594     case _sv_blockade:
1595         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1596         {
1597             blockade = (float)ltemp;
1598         }
1599         break;
1600     case _sv_shadowcolor:
1601         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1602         {
1603             shadowcolor = (int)ltemp;
1604         }
1605         break;
1606     case _sv_shadowalpha:
1607         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1608         {
1609             shadowalpha = (int)ltemp;
1610         }
1611         break;
1612     case _sv_shadowopacity:
1613         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1614         {
1615             shadowopacity = (int)ltemp;
1616         }
1617         break;
1618     case _sv_skiptoset:
1619         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1620         {
1621             skiptoset = (int)ltemp;
1622         }
1623         break;
1624     case _sv_slowmotion:
1625         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1626         {
1627             slowmotion.toggle = (unsigned)ltemp;
1628         }
1629         break;
1630     case _sv_slowmotion_duration:
1631         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1632         {
1633             slowmotion.duration = (unsigned)ltemp;
1634         }
1635         break;
1636     case _sv_lasthita:
1637     case _sv_lasthity:
1638         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1639         {
1640             lasthit.position.y = (float)ltemp;
1641         }
1642         break;
1643     case _sv_lasthitx:
1644         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1645         {
1646             lasthit.position.x = (float)ltemp;
1647         }
1648         break;
1649     case _sv_lasthitz:
1650         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1651         {
1652             lasthit.position.z = (float)ltemp;
1653         }
1654         break;
1655     case _sv_lasthitc:
1656         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1657         {
1658             lasthit.confirm = (int)ltemp;
1659         }
1660         break;
1661     case _sv_lasthitt:
1662         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1663         {
1664             lasthit.attack->attack_type = (int)ltemp;
1665         }
1666         break;
1667     case _sv_smartbomber:
1668         smartbomber = (entity *)value->ptrVal;
1669         break;
1670     case _sv_textbox:
1671         textbox = (entity *)value->ptrVal;
1672         break;
1673     case _sv_background:
1674         background = (s_screen *)value->ptrVal;
1675         break;
1676     case _sv_vscreen:
1677         vscreen = (s_screen *)value->ptrVal;
1678         break;
1679     case _sv_nofadeout:
1680         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1681         {
1682             nofadeout = (int)ltemp;
1683         }
1684         break;
1685     case _sv_nojoin:
1686         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1687         {
1688             nojoin = (int)ltemp;
1689         }
1690         break;
1691     case _sv_nopause:
1692         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1693         {
1694             nopause = (int)ltemp;
1695         }
1696         break;
1697     case _sv_nosave:
1698         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1699         {
1700             nosave = (int)ltemp;
1701         }
1702         break;
1703     case _sv_noscreenshot:
1704         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1705         {
1706             noscreenshot = (int)ltemp;
1707         }
1708         break;
1709     case _sv_noshowcomplete:
1710         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1711         {
1712             levelsets[current_set].noshowcomplete = (int)ltemp;
1713         }
1714         break;
1715     case _sv_viewportx:
1716         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1717         {
1718             viewportx = (int)ltemp;
1719         }
1720         break;
1721     case _sv_viewporty:
1722         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1723         {
1724             viewporty = (int)ltemp;
1725         }
1726         break;
1727     case _sv_viewportw:
1728         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1729         {
1730             viewportw = (int)ltemp;
1731         }
1732         break;
1733     case _sv_viewporth:
1734         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1735         {
1736             viewporth = (int)ltemp;
1737         }
1738         break;
1739     case _sv_waiting:
1740         if(!level)
1741         {
1742             break;
1743         }
1744         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1745         {
1746             level->waiting = (int)ltemp;
1747         }
1748         break;
1749     case _sv_nogameover:
1750         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1751         {
1752             levelsets[current_set].noshowgameover = (int)ltemp;
1753         }
1754         break;
1755     case _sv_nohof:
1756         if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp)))
1757         {
1758             levelsets[current_set].noshowhof = (int)ltemp;
1759         }
1760         break;
1761     default:
1762         return 0;
1763     }
1764 
1765     return 1;
1766 }
1767 
1768 
load_script(Script * script,char * file)1769 int load_script(Script *script, char *file)
1770 {
1771     size_t size = 0;
1772     int failed = 0;
1773     char *buf = NULL;
1774 
1775     if(buffer_pakfile(file, &buf, &size) != 1)
1776     {
1777         return 0;
1778     }
1779 
1780     failed = !Script_AppendText(script, buf, file);
1781 
1782     if(buf != NULL)
1783     {
1784         free(buf);
1785         buf = NULL;
1786     }
1787 
1788     // text loaded but parsing failed, shutdown
1789     if(failed)
1790     {
1791         shutdown(1, "Failed to parse script file: '%s'!\n", file);
1792     }
1793     return !failed;
1794 }
1795 
1796 // this method is used by load_scripts, don't call it
init_scripts()1797 void init_scripts()
1798 {
1799     int i;
1800     Script_Global_Init();
1801     Script_Init(&update_script,     "update",  NULL,  1);
1802     Script_Init(&updated_script,    "updated",  NULL, 1);
1803     Script_Init(&level_script,      "level",    NULL,  1);
1804     Script_Init(&endlevel_script,   "endlevel",  NULL, 1);
1805     Script_Init(&key_script_all,    "keyall",   NULL,  1);
1806     Script_Init(&timetick_script,   "timetick",  NULL, 1);
1807     Script_Init(&loading_script,    "loading",   NULL, 1);
1808     for(i = 0; i < 4; i++)
1809     {
1810         Script_Init(&score_script[i],    "score",    NULL,  1);
1811     }
1812     for(i = 0; i < 4; i++)
1813     {
1814         Script_Init(&key_script[i],      "key",      NULL,  1);
1815     }
1816     for(i = 0; i < 4; i++)
1817     {
1818         Script_Init(&join_script[i],     "join",      NULL, 1);
1819     }
1820     for(i = 0; i < 4; i++)
1821     {
1822         Script_Init(&respawn_script[i],  "respawn",   NULL, 1);
1823     }
1824     for(i = 0; i < 4; i++)
1825     {
1826         Script_Init(&pdie_script[i],     "die",       NULL, 1);
1827     }
1828 }
1829 
1830 // This method is called once when the engine starts, do not use it multiple times
1831 // It should be calld after load_script_setting
load_scripts()1832 void load_scripts()
1833 {
1834     int i;
1835     init_scripts();
1836     //Script_Clear's second parameter set to 2, because the script fails to load,
1837     //and will never have another chance to be loaded, so just clear the variable list in it
1838     if(!load_script(&update_script,     "data/scripts/update.c"))
1839     {
1840         Script_Clear(&update_script,        2);
1841     }
1842     if(!load_script(&updated_script,    "data/scripts/updated.c"))
1843     {
1844         Script_Clear(&updated_script,       2);
1845     }
1846     if(!load_script(&level_script,      "data/scripts/level.c"))
1847     {
1848         Script_Clear(&level_script,         2);
1849     }
1850     if(!load_script(&endlevel_script,   "data/scripts/endlevel.c"))
1851     {
1852         Script_Clear(&endlevel_script,      2);
1853     }
1854     if(!load_script(&key_script_all,    "data/scripts/keyall.c"))
1855     {
1856         Script_Clear(&key_script_all,       2);
1857     }
1858     if(!load_script(&timetick_script,   "data/scripts/timetick.c"))
1859     {
1860         Script_Clear(&timetick_script,      2);
1861     }
1862     if(!load_script(&loading_script,    "data/scripts/loading.c"))
1863     {
1864         Script_Clear(&loading_script,       2);
1865     }
1866     if(!load_script(&score_script[0],   "data/scripts/score1.c"))
1867     {
1868         Script_Clear(&score_script[0],      2);
1869     }
1870     if(!load_script(&score_script[1],   "data/scripts/score2.c"))
1871     {
1872         Script_Clear(&score_script[1],      2);
1873     }
1874     if(!load_script(&score_script[2],   "data/scripts/score3.c"))
1875     {
1876         Script_Clear(&score_script[2],      2);
1877     }
1878     if(!load_script(&score_script[3],   "data/scripts/score4.c"))
1879     {
1880         Script_Clear(&score_script[3],      2);
1881     }
1882     if(!load_script(&key_script[0],     "data/scripts/key1.c"))
1883     {
1884         Script_Clear(&key_script[0],        2);
1885     }
1886     if(!load_script(&key_script[1],     "data/scripts/key2.c"))
1887     {
1888         Script_Clear(&key_script[1],        2);
1889     }
1890     if(!load_script(&key_script[2],     "data/scripts/key3.c"))
1891     {
1892         Script_Clear(&key_script[2],        2);
1893     }
1894     if(!load_script(&key_script[3],     "data/scripts/key4.c"))
1895     {
1896         Script_Clear(&key_script[3],        2);
1897     }
1898     if(!load_script(&join_script[0],    "data/scripts/join1.c"))
1899     {
1900         Script_Clear(&join_script[0],       2);
1901     }
1902     if(!load_script(&join_script[1],    "data/scripts/join2.c"))
1903     {
1904         Script_Clear(&join_script[1],       2);
1905     }
1906     if(!load_script(&join_script[2],    "data/scripts/join3.c"))
1907     {
1908         Script_Clear(&join_script[2],       2);
1909     }
1910     if(!load_script(&join_script[3],    "data/scripts/join4.c"))
1911     {
1912         Script_Clear(&join_script[3],       2);
1913     }
1914     if(!load_script(&respawn_script[0], "data/scripts/respawn1.c"))
1915     {
1916         Script_Clear(&respawn_script[0],    2);
1917     }
1918     if(!load_script(&respawn_script[1], "data/scripts/respawn2.c"))
1919     {
1920         Script_Clear(&respawn_script[1],    2);
1921     }
1922     if(!load_script(&respawn_script[2], "data/scripts/respawn3.c"))
1923     {
1924         Script_Clear(&respawn_script[2],    2);
1925     }
1926     if(!load_script(&respawn_script[3], "data/scripts/respawn4.c"))
1927     {
1928         Script_Clear(&respawn_script[3],    2);
1929     }
1930     if(!load_script(&pdie_script[0],    "data/scripts/die1.c"))
1931     {
1932         Script_Clear(&pdie_script[0],       2);
1933     }
1934     if(!load_script(&pdie_script[1],    "data/scripts/die2.c"))
1935     {
1936         Script_Clear(&pdie_script[1],       2);
1937     }
1938     if(!load_script(&pdie_script[2],    "data/scripts/die3.c"))
1939     {
1940         Script_Clear(&pdie_script[2],       2);
1941     }
1942     if(!load_script(&pdie_script[3],    "data/scripts/die4.c"))
1943     {
1944         Script_Clear(&pdie_script[3],       2);
1945     }
1946     Script_Compile(&update_script);
1947     Script_Compile(&updated_script);
1948     Script_Compile(&level_script);
1949     Script_Compile(&endlevel_script);
1950     Script_Compile(&key_script_all);
1951     Script_Compile(&timetick_script);
1952     Script_Compile(&loading_script);
1953     for(i = 0; i < 4; i++)
1954     {
1955         Script_Compile(&score_script[i]);
1956     }
1957     for(i = 0; i < 4; i++)
1958     {
1959         Script_Compile(&key_script[i]);
1960     }
1961     for(i = 0; i < 4; i++)
1962     {
1963         Script_Compile(&join_script[i]);
1964     }
1965     for(i = 0; i < 4; i++)
1966     {
1967         Script_Compile(&respawn_script[i]);
1968     }
1969     for(i = 0; i < 4; i++)
1970     {
1971         Script_Compile(&pdie_script[i]);
1972     }
1973 }
1974 
1975 // This method is called once when the engine is shutting down, do not use it multiple times
clear_scripts()1976 void clear_scripts()
1977 {
1978     int i;
1979     //Script_Clear's second parameter set to 2, because the script fails to load,
1980     //and will never have another chance to be loaded, so just clear the variable list in it
1981     Script_Clear(&update_script,    2);
1982     Script_Clear(&updated_script,   2);
1983     Script_Clear(&level_script,     2);
1984     Script_Clear(&endlevel_script,  2);
1985     Script_Clear(&key_script_all,   2);
1986     Script_Clear(&timetick_script,  2);
1987     Script_Clear(&loading_script,   2);
1988     for(i = 0; i < 4; i++)
1989     {
1990         Script_Clear(&score_script[i],      2);
1991     }
1992     for(i = 0; i < 4; i++)
1993     {
1994         Script_Clear(&key_script[i],        2);
1995     }
1996     for(i = 0; i < 4; i++)
1997     {
1998         Script_Clear(&join_script[i],       2);
1999     }
2000     for(i = 0; i < 4; i++)
2001     {
2002         Script_Clear(&respawn_script[i],    2);
2003     }
2004     for(i = 0; i < 4; i++)
2005     {
2006         Script_Clear(&pdie_script[i],       2);
2007     }
2008     Script_Global_Clear();
2009 }
2010 
2011 #define scripts_membercount (sizeof(s_scripts) / sizeof(Script*))
2012 
alloc_all_scripts(s_scripts ** s)2013 void alloc_all_scripts(s_scripts **s)
2014 {
2015     size_t i;
2016 
2017     if(!(*s))
2018     {
2019         *s = (s_scripts *)malloc(sizeof(s_scripts));
2020         for (i = 0; i < scripts_membercount; i++)
2021         {
2022             (((Script **) (*s))[i]) = alloc_script();
2023         }
2024     }
2025 }
2026 
clear_all_scripts(s_scripts * s,int method)2027 void clear_all_scripts(s_scripts *s, int method)
2028 {
2029     size_t i;
2030     Script **ps = (Script **) s;
2031 
2032     for (i = 0; i < scripts_membercount; i++)
2033     {
2034         Script_Clear(ps[i],   method);
2035     }
2036 }
2037 
free_all_scripts(s_scripts ** s)2038 void free_all_scripts(s_scripts **s)
2039 {
2040     size_t i;
2041     Script **ps = (Script **) (*s);
2042 
2043     for (i = 0; i < scripts_membercount; i++)
2044     {
2045         if (ps[i])
2046         {
2047             free(ps[i]);
2048             ps[i] = NULL;
2049         }
2050     }
2051     free(*s);
2052     *s = NULL;
2053 }
2054 
copy_all_scripts(s_scripts * src,s_scripts * dest,int method)2055 void copy_all_scripts(s_scripts *src, s_scripts *dest, int method)
2056 {
2057     size_t i;
2058     Script **ps = (Script **) src;
2059     Script **pd = (Script **) dest;
2060 
2061     for (i = 0; i < scripts_membercount; i++)
2062     {
2063         Script_Copy(pd[i], ps[i], method);
2064     }
2065 }
2066 
execute_animation_script(entity * ent)2067 void execute_animation_script(entity *ent)
2068 {
2069     ScriptVariant tempvar;
2070     int is1 = 0, is2 = 0;
2071     char *namelist[] = {"self", "animnum", "frame", "animhandle", ""};
2072     int handle = 0;
2073     Script *cs = ent->scripts->animation_script;
2074     Script *s1 = ent->model->scripts->animation_script;
2075     Script *s2 = ent->defaultmodel->scripts->animation_script;
2076     is1 = Script_IsInitialized(s1);
2077     is2 = Script_IsInitialized(s2);
2078     if(is1 || is2)
2079     {
2080         if(cs->pinterpreter && cs->pinterpreter->bReset)
2081         {
2082             handle = Script_Save_Local_Variant(cs, namelist);
2083         }
2084         ScriptVariant_Init(&tempvar);
2085         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2086         tempvar.ptrVal = (VOID *)ent;
2087         Script_Set_Local_Variant(cs, "self",    &tempvar);
2088         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2089         tempvar.lVal = (LONG)ent->animnum;
2090         Script_Set_Local_Variant(cs, "animnum", &tempvar);
2091         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2092         tempvar.lVal = (LONG)ent->animpos;
2093         Script_Set_Local_Variant(cs, "frame",   &tempvar);
2094         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2095         tempvar.lVal = (LONG)ent->animation->index;
2096         Script_Set_Local_Variant(cs, "animhandle",   &tempvar);
2097         if(is1)
2098         {
2099             Script_Copy(cs, s1, 0);
2100             Script_Execute(cs);
2101         }
2102         if(ent->model != ent->defaultmodel && is2)
2103         {
2104             Script_Copy(cs, s2, 0);
2105             Script_Execute(cs);
2106         }
2107         //clear to save variant space
2108         ScriptVariant_Clear(&tempvar);
2109         Script_Set_Local_Variant(cs, "self",    &tempvar);
2110         Script_Set_Local_Variant(cs, "animnum", &tempvar);
2111         Script_Set_Local_Variant(cs, "frame",   &tempvar);
2112         Script_Set_Local_Variant(cs, "animhandle", &tempvar);
2113         if(handle)
2114         {
2115             Script_Load_Local_Variant(cs, handle);
2116         }
2117     }
2118 }
2119 
execute_takedamage_script(entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int tag)2120 void execute_takedamage_script(entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int tag)
2121 {
2122     ScriptVariant tempvar;
2123     Script *cs = ent->scripts->takedamage_script;
2124     if(Script_IsInitialized(cs))
2125     {
2126         ScriptVariant_Init(&tempvar);
2127         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2128         tempvar.ptrVal = (VOID *)ent;
2129         Script_Set_Local_Variant(cs, "self",        &tempvar);
2130         tempvar.ptrVal = (VOID *)other;
2131         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2132         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2133         tempvar.lVal = (LONG)force;
2134         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2135         tempvar.lVal = (LONG)drop;
2136         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2137         tempvar.lVal = (LONG)type;
2138         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2139         tempvar.lVal = (LONG)noblock;
2140         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2141         tempvar.lVal = (LONG)guardcost;
2142         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2143         tempvar.lVal = (LONG)jugglecost;
2144         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2145         tempvar.lVal = (LONG)pauseadd;
2146         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2147         tempvar.lVal = (LONG)tag;
2148         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2149         Script_Execute(cs);
2150         //clear to save variant space
2151         ScriptVariant_Clear(&tempvar);
2152         Script_Set_Local_Variant(cs, "self",        &tempvar);
2153         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2154         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2155         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2156         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2157         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2158         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2159         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2160         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2161         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2162     }
2163 }
2164 
execute_onpain_script(entity * ent,int iType,int iReset)2165 void execute_onpain_script(entity *ent, int iType, int iReset)
2166 {
2167     ScriptVariant tempvar;
2168     Script *cs = ent->scripts->onpain_script;
2169     if(Script_IsInitialized(cs))
2170     {
2171         ScriptVariant_Init(&tempvar);
2172         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2173         tempvar.ptrVal = (VOID *)ent;
2174         Script_Set_Local_Variant(cs, "self",        &tempvar);
2175         tempvar.lVal = (LONG)iType;
2176         Script_Set_Local_Variant(cs, "attacktype",   &tempvar);
2177         tempvar.lVal = (LONG)iReset;
2178         Script_Set_Local_Variant(cs, "reset",       &tempvar);
2179         Script_Execute(cs);
2180         //clear to save variant space
2181         ScriptVariant_Clear(&tempvar);
2182         Script_Set_Local_Variant(cs, "self",        &tempvar);
2183         Script_Set_Local_Variant(cs, "type",        &tempvar);
2184         Script_Set_Local_Variant(cs, "reset",       &tempvar);
2185     }
2186 }
2187 
execute_onfall_script(entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int tag)2188 void execute_onfall_script(entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int tag)
2189 {
2190     ScriptVariant tempvar;
2191     Script *cs = ent->scripts->onfall_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 *)other;
2199         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2200         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2201         tempvar.lVal = (LONG)force;
2202         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2203         tempvar.lVal = (LONG)drop;
2204         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2205         tempvar.lVal = (LONG)type;
2206         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2207         tempvar.lVal = (LONG)noblock;
2208         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2209         tempvar.lVal = (LONG)guardcost;
2210         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2211         tempvar.lVal = (LONG)jugglecost;
2212         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2213         tempvar.lVal = (LONG)pauseadd;
2214         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2215         tempvar.lVal = (LONG)tag;
2216         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2217         Script_Execute(cs);
2218         //clear to save variant space
2219         ScriptVariant_Clear(&tempvar);
2220         Script_Set_Local_Variant(cs, "self",        &tempvar);
2221         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2222         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2223         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2224         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2225         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2226         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2227         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2228         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2229         Script_Set_Local_Variant(cs, "tag",         &tempvar);
2230     }
2231 }
2232 
execute_onblocks_script(entity * ent)2233 void execute_onblocks_script(entity *ent)
2234 {
2235     ScriptVariant tempvar;
2236     Script *cs = ent->scripts->onblocks_script;
2237     if(Script_IsInitialized(cs))
2238     {
2239         ScriptVariant_Init(&tempvar);
2240         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2241         tempvar.ptrVal = (VOID *)ent;
2242         Script_Set_Local_Variant(cs, "self", &tempvar);
2243         Script_Execute(cs);
2244 
2245         //clear to save variant space
2246         ScriptVariant_Clear(&tempvar);
2247         Script_Set_Local_Variant(cs, "self", &tempvar);
2248     }
2249 }
2250 
execute_onblockw_script(entity * ent,int plane,float height,int index)2251 void execute_onblockw_script(entity *ent, int plane, float height, int index)
2252 {
2253     ScriptVariant tempvar;
2254     Script *cs = ent->scripts->onblockw_script;
2255     if(Script_IsInitialized(cs))
2256     {
2257         ScriptVariant_Init(&tempvar);
2258         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2259         tempvar.ptrVal = (VOID *)ent;
2260         Script_Set_Local_Variant(cs, "self", &tempvar);
2261         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2262         tempvar.lVal = (LONG)plane;
2263         Script_Set_Local_Variant(cs, "plane",      &tempvar);
2264         ScriptVariant_ChangeType(&tempvar, VT_DECIMAL);
2265         tempvar.dblVal = (DOUBLE)height;
2266         Script_Set_Local_Variant(cs, "height",      &tempvar);
2267         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2268         tempvar.lVal = (LONG)index;
2269         Script_Set_Local_Variant(cs, "index",      &tempvar);
2270         Script_Execute(cs);
2271 
2272         //clear to save variant space
2273         ScriptVariant_Clear(&tempvar);
2274         Script_Set_Local_Variant(cs, "self", &tempvar);
2275         ScriptVariant_Clear(&tempvar);
2276         Script_Set_Local_Variant(cs, "plane", &tempvar);
2277         ScriptVariant_Clear(&tempvar);
2278         Script_Set_Local_Variant(cs, "height", &tempvar);
2279         ScriptVariant_Clear(&tempvar);
2280         Script_Set_Local_Variant(cs, "index", &tempvar);
2281     }
2282 }
2283 
execute_inhole_script(entity * ent,int plane,float height,int index)2284 void execute_inhole_script(entity *ent, int plane, float height, int index)
2285 {
2286     ScriptVariant tempvar;
2287     Script *cs = ent->scripts->inhole_script;
2288     if(Script_IsInitialized(cs))
2289     {
2290         ScriptVariant_Init(&tempvar);
2291         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2292         tempvar.ptrVal = (VOID *)ent;
2293         Script_Set_Local_Variant(cs, "self", &tempvar);
2294         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2295         tempvar.lVal = (LONG)plane;
2296         Script_Set_Local_Variant(cs, "plane",      &tempvar);
2297         ScriptVariant_ChangeType(&tempvar, VT_DECIMAL);
2298         tempvar.dblVal = (DOUBLE)height;
2299         Script_Set_Local_Variant(cs, "height",      &tempvar);
2300         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2301         tempvar.lVal = (LONG)index;
2302         Script_Set_Local_Variant(cs, "index",      &tempvar);
2303         Script_Execute(cs);
2304 
2305         //clear to save variant space
2306         ScriptVariant_Clear(&tempvar);
2307         Script_Set_Local_Variant(cs, "self", &tempvar);
2308         ScriptVariant_Clear(&tempvar);
2309         Script_Set_Local_Variant(cs, "plane", &tempvar);
2310         ScriptVariant_Clear(&tempvar);
2311         Script_Set_Local_Variant(cs, "height", &tempvar);
2312         ScriptVariant_Clear(&tempvar);
2313         Script_Set_Local_Variant(cs, "index", &tempvar);
2314     }
2315 }
2316 
execute_onblockp_script(entity * ent,int plane,entity * platform)2317 void execute_onblockp_script(entity *ent, int plane, entity *platform)
2318 {
2319     ScriptVariant tempvar;
2320     Script *cs = ent->scripts->onblockp_script;
2321     if(Script_IsInitialized(cs))
2322     {
2323         ScriptVariant_Init(&tempvar);
2324         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2325         tempvar.ptrVal = (VOID *)ent;
2326         Script_Set_Local_Variant(cs, "self", &tempvar);
2327         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2328         tempvar.lVal = (LONG)plane;
2329         Script_Set_Local_Variant(cs, "plane",      &tempvar);
2330         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2331         tempvar.ptrVal = (VOID *)platform;
2332         Script_Set_Local_Variant(cs, "platform",      &tempvar);
2333         Script_Execute(cs);
2334 
2335         //clear to save variant space
2336         ScriptVariant_Clear(&tempvar);
2337         Script_Set_Local_Variant(cs, "self", &tempvar);
2338         ScriptVariant_Clear(&tempvar);
2339         Script_Set_Local_Variant(cs, "plane", &tempvar);
2340         ScriptVariant_Clear(&tempvar);
2341         Script_Set_Local_Variant(cs, "platform", &tempvar);
2342     }
2343 }
2344 
execute_onblocko_script(entity * ent,entity * other)2345 void execute_onblocko_script(entity *ent, entity *other)
2346 {
2347     ScriptVariant tempvar;
2348     Script *cs = ent->scripts->onblocko_script;
2349     if(Script_IsInitialized(cs))
2350     {
2351         ScriptVariant_Init(&tempvar);
2352         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2353         tempvar.ptrVal = (VOID *)ent;
2354         Script_Set_Local_Variant(cs, "self",        &tempvar);
2355         tempvar.ptrVal = (VOID *)other;
2356         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
2357         Script_Execute(cs);
2358 
2359         //clear to save variant space
2360         ScriptVariant_Clear(&tempvar);
2361         Script_Set_Local_Variant(cs, "self",        &tempvar);
2362         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
2363     }
2364 }
2365 
execute_onblockz_script(entity * ent)2366 void execute_onblockz_script(entity *ent)
2367 {
2368     ScriptVariant tempvar;
2369     Script *cs = ent->scripts->onblockz_script;
2370     if(Script_IsInitialized(cs))
2371     {
2372         ScriptVariant_Init(&tempvar);
2373         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2374         tempvar.ptrVal = (VOID *)ent;
2375         Script_Set_Local_Variant(cs, "self", &tempvar);
2376         Script_Execute(cs);
2377 
2378         //clear to save variant space
2379         ScriptVariant_Clear(&tempvar);
2380         Script_Set_Local_Variant(cs, "self", &tempvar);
2381     }
2382 }
2383 
execute_onblocka_script(entity * ent,entity * other)2384 void execute_onblocka_script(entity *ent, entity *other)
2385 {
2386     ScriptVariant tempvar;
2387     Script *cs = ent->scripts->onblocka_script;
2388     if(Script_IsInitialized(cs))
2389     {
2390         ScriptVariant_Init(&tempvar);
2391         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2392         tempvar.ptrVal = (VOID *)ent;
2393         Script_Set_Local_Variant(cs, "self",        &tempvar);
2394         tempvar.ptrVal = (VOID *)other;
2395         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
2396         Script_Execute(cs);
2397         //clear to save variant space
2398         ScriptVariant_Clear(&tempvar);
2399         Script_Set_Local_Variant(cs, "self",        &tempvar);
2400         Script_Set_Local_Variant(cs, "obstacle",    &tempvar);
2401     }
2402 }
2403 
execute_onmovex_script(entity * ent)2404 void execute_onmovex_script(entity *ent)
2405 {
2406     ScriptVariant tempvar;
2407     Script *cs = ent->scripts->onmovex_script;
2408     if(Script_IsInitialized(cs))
2409     {
2410         ScriptVariant_Init(&tempvar);
2411         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2412         tempvar.ptrVal = (VOID *)ent;
2413         Script_Set_Local_Variant(cs, "self", &tempvar);
2414         Script_Execute(cs);
2415 
2416         //clear to save variant space
2417         ScriptVariant_Clear(&tempvar);
2418         Script_Set_Local_Variant(cs, "self", &tempvar);
2419     }
2420 }
2421 
execute_onmovez_script(entity * ent)2422 void execute_onmovez_script(entity *ent)
2423 {
2424     ScriptVariant tempvar;
2425     Script *cs = ent->scripts->onmovez_script;
2426     if(Script_IsInitialized(cs))
2427     {
2428         ScriptVariant_Init(&tempvar);
2429         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2430         tempvar.ptrVal = (VOID *)ent;
2431         Script_Set_Local_Variant(cs, "self", &tempvar);
2432         Script_Execute(cs);
2433 
2434         //clear to save variant space
2435         ScriptVariant_Clear(&tempvar);
2436         Script_Set_Local_Variant(cs, "self", &tempvar);
2437     }
2438 }
2439 
execute_onmovea_script(entity * ent)2440 void execute_onmovea_script(entity *ent)
2441 {
2442     ScriptVariant tempvar;
2443     Script *cs = ent->scripts->onmovea_script;
2444     if(Script_IsInitialized(cs))
2445     {
2446         ScriptVariant_Init(&tempvar);
2447         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2448         tempvar.ptrVal = (VOID *)ent;
2449         Script_Set_Local_Variant(cs, "self", &tempvar);
2450         Script_Execute(cs);
2451 
2452         //clear to save variant space
2453         ScriptVariant_Clear(&tempvar);
2454         Script_Set_Local_Variant(cs, "self", &tempvar);
2455     }
2456 }
2457 
execute_ondeath_script(entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int tag)2458 void execute_ondeath_script(entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int tag)
2459 {
2460     ScriptVariant tempvar;
2461     Script *cs = ent->scripts->ondeath_script;
2462     if(Script_IsInitialized(cs))
2463     {
2464         ScriptVariant_Init(&tempvar);
2465         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2466         tempvar.ptrVal = (VOID *)ent;
2467         Script_Set_Local_Variant(cs, "self",        &tempvar);
2468         tempvar.ptrVal = (VOID *)other;
2469         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2470         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2471         tempvar.lVal = (LONG)force;
2472         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2473         tempvar.lVal = (LONG)drop;
2474         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2475         tempvar.lVal = (LONG)type;
2476         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2477         tempvar.lVal = (LONG)noblock;
2478         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2479         tempvar.lVal = (LONG)guardcost;
2480         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2481         tempvar.lVal = (LONG)jugglecost;
2482         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2483         tempvar.lVal = (LONG)pauseadd;
2484         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2485         tempvar.lVal = (LONG)tag;
2486         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2487         Script_Execute(cs);
2488         //clear to save variant space
2489         ScriptVariant_Clear(&tempvar);
2490         Script_Set_Local_Variant(cs, "self",        &tempvar);
2491         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2492         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2493         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2494         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2495         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2496         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2497         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2498         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2499         Script_Set_Local_Variant(cs, "tag",         &tempvar);
2500     }
2501 }
2502 
execute_onkill_script(entity * ent)2503 void execute_onkill_script(entity *ent)
2504 {
2505     ScriptVariant tempvar;
2506     Script *cs = ent->scripts->onkill_script;
2507     if(Script_IsInitialized(cs))
2508     {
2509         ScriptVariant_Init(&tempvar);
2510         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2511         tempvar.ptrVal = (VOID *)ent;
2512         Script_Set_Local_Variant(cs, "self", &tempvar);
2513         Script_Execute(cs);
2514         //clear to save variant space
2515         ScriptVariant_Clear(&tempvar);
2516         Script_Set_Local_Variant(cs, "self", &tempvar);
2517     }
2518 }
2519 
execute_didblock_script(entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int tag)2520 void execute_didblock_script(entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int tag)
2521 {
2522     ScriptVariant tempvar;
2523     Script *cs = ent->scripts->didblock_script;
2524     if(Script_IsInitialized(cs))
2525     {
2526         ScriptVariant_Init(&tempvar);
2527         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2528         tempvar.ptrVal = (VOID *)ent;
2529         Script_Set_Local_Variant(cs, "self",        &tempvar);
2530         tempvar.ptrVal = (VOID *)other;
2531         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2532         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2533         tempvar.lVal = (LONG)force;
2534         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2535         tempvar.lVal = (LONG)drop;
2536         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2537         tempvar.lVal = (LONG)type;
2538         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2539         tempvar.lVal = (LONG)noblock;
2540         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2541         tempvar.lVal = (LONG)guardcost;
2542         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2543         tempvar.lVal = (LONG)jugglecost;
2544         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2545         tempvar.lVal = (LONG)pauseadd;
2546         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2547         tempvar.lVal = (LONG)tag;
2548         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2549         Script_Execute(cs);
2550         //clear to save variant space
2551         ScriptVariant_Clear(&tempvar);
2552         Script_Set_Local_Variant(cs, "self",        &tempvar);
2553         Script_Set_Local_Variant(cs, "attacker",    &tempvar);
2554         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2555         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2556         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2557         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2558         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2559         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2560         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2561         Script_Set_Local_Variant(cs, "tag",         &tempvar);
2562     }
2563 }
2564 
execute_ondoattack_script(entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int iWhich,int iAtkID,int tag)2565 void execute_ondoattack_script(entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int iWhich, int iAtkID, int tag)
2566 {
2567     ScriptVariant tempvar;
2568     Script *cs = ent->scripts->ondoattack_script;
2569     if(Script_IsInitialized(cs))
2570     {
2571         ScriptVariant_Init(&tempvar);
2572         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2573         tempvar.ptrVal = (VOID *)ent;
2574         Script_Set_Local_Variant(cs, "self",        &tempvar);
2575         tempvar.ptrVal = (VOID *)other;
2576         Script_Set_Local_Variant(cs, "other",    &tempvar);
2577         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2578         tempvar.lVal = (LONG)force;
2579         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2580         tempvar.lVal = (LONG)drop;
2581         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2582         tempvar.lVal = (LONG)type;
2583         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2584         tempvar.lVal = (LONG)noblock;
2585         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2586         tempvar.lVal = (LONG)guardcost;
2587         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2588         tempvar.lVal = (LONG)jugglecost;
2589         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2590         tempvar.lVal = (LONG)pauseadd;
2591         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2592         tempvar.lVal = (LONG)iWhich;
2593         Script_Set_Local_Variant(cs, "which",    &tempvar);
2594         tempvar.lVal = (LONG)iAtkID;
2595         Script_Set_Local_Variant(cs, "attackid",    &tempvar);
2596         tempvar.lVal = (LONG)tag;
2597         Script_Set_Local_Variant(cs, "tag",    &tempvar);
2598         Script_Execute(cs);
2599         //clear to save variant space
2600         ScriptVariant_Clear(&tempvar);
2601         Script_Set_Local_Variant(cs, "self",        &tempvar);
2602         Script_Set_Local_Variant(cs, "other",		&tempvar);
2603         Script_Set_Local_Variant(cs, "damage",      &tempvar);
2604         Script_Set_Local_Variant(cs, "drop",        &tempvar);
2605         Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2606         Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2607         Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2608         Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2609         Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2610         Script_Set_Local_Variant(cs, "which",		&tempvar);
2611         Script_Set_Local_Variant(cs, "attackid",	&tempvar);
2612         Script_Set_Local_Variant(cs, "tag",	        &tempvar);
2613     }
2614 }
2615 
execute_updateentity_script(entity * ent)2616 void execute_updateentity_script(entity *ent)
2617 {
2618     ScriptVariant tempvar;
2619     Script *cs = ent->scripts->update_script;
2620     if(Script_IsInitialized(cs))
2621     {
2622         ScriptVariant_Init(&tempvar);
2623         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2624         tempvar.ptrVal = (VOID *)ent;
2625         Script_Set_Local_Variant(cs, "self", &tempvar);
2626         Script_Execute(cs);
2627         //clear to save variant space
2628         ScriptVariant_Clear(&tempvar);
2629         Script_Set_Local_Variant(cs, "self", &tempvar);
2630     }
2631 }
2632 
execute_think_script(entity * ent)2633 void execute_think_script(entity *ent)
2634 {
2635     ScriptVariant tempvar;
2636     Script *cs = ent->scripts->think_script;
2637     if(Script_IsInitialized(cs))
2638     {
2639         ScriptVariant_Init(&tempvar);
2640         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2641         tempvar.ptrVal = (VOID *)ent;
2642         Script_Set_Local_Variant(cs, "self", &tempvar);
2643         Script_Execute(cs);
2644         //clear to save variant space
2645         ScriptVariant_Clear(&tempvar);
2646         Script_Set_Local_Variant(cs, "self", &tempvar);
2647     }
2648 }
2649 
_execute_didhit_script(Script * cs,entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int blocked,int tag)2650 static void _execute_didhit_script(Script *cs, entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int blocked, int tag)
2651 {
2652     ScriptVariant tempvar;
2653     ScriptVariant_Init(&tempvar);
2654     ScriptVariant_ChangeType(&tempvar, VT_PTR);
2655     tempvar.ptrVal = (VOID *)ent;
2656     Script_Set_Local_Variant(cs, "self",        &tempvar);
2657     tempvar.ptrVal = (VOID *)other;
2658     Script_Set_Local_Variant(cs, "damagetaker", &tempvar);
2659     ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2660     tempvar.lVal = (LONG)force;
2661     Script_Set_Local_Variant(cs, "damage",      &tempvar);
2662     tempvar.lVal = (LONG)drop;
2663     Script_Set_Local_Variant(cs, "drop",        &tempvar);
2664     tempvar.lVal = (LONG)type;
2665     Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2666     tempvar.lVal = (LONG)noblock;
2667     Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2668     tempvar.lVal = (LONG)guardcost;
2669     Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2670     tempvar.lVal = (LONG)jugglecost;
2671     Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2672     tempvar.lVal = (LONG)pauseadd;
2673     Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2674     tempvar.lVal = (LONG)blocked;
2675     Script_Set_Local_Variant(cs, "blocked",     &tempvar);
2676     tempvar.lVal = (LONG)tag;
2677     Script_Set_Local_Variant(cs, "tag",         &tempvar);
2678     Script_Execute(cs);
2679     //clear to save variant space
2680     ScriptVariant_Clear(&tempvar);
2681     Script_Set_Local_Variant(cs, "self",        &tempvar);
2682     Script_Set_Local_Variant(cs, "damagetaker", &tempvar);
2683     Script_Set_Local_Variant(cs, "damage",      &tempvar);
2684     Script_Set_Local_Variant(cs, "drop",        &tempvar);
2685     Script_Set_Local_Variant(cs, "attacktype",  &tempvar);
2686     Script_Set_Local_Variant(cs, "noblock",     &tempvar);
2687     Script_Set_Local_Variant(cs, "guardcost",   &tempvar);
2688     Script_Set_Local_Variant(cs, "jugglecost",  &tempvar);
2689     Script_Set_Local_Variant(cs, "pauseadd",    &tempvar);
2690     Script_Set_Local_Variant(cs, "blocked",     &tempvar);
2691     Script_Set_Local_Variant(cs, "tag",         &tempvar);
2692 }
2693 
execute_didhit_script(entity * ent,entity * other,int force,int drop,int type,int noblock,int guardcost,int jugglecost,int pauseadd,int blocked,int tag)2694 void execute_didhit_script(entity *ent, entity *other, int force, int drop, int type, int noblock, int guardcost, int jugglecost, int pauseadd, int blocked, int tag)
2695 {
2696     Script *cs;
2697     s_scripts *gs = global_model_scripts;
2698     if(gs && (cs = gs->didhit_script) && Script_IsInitialized(cs))
2699     {
2700         _execute_didhit_script(cs, ent, other, force, drop, type, noblock, guardcost, jugglecost, pauseadd, blocked, tag);
2701     }
2702     if(Script_IsInitialized(cs = ent->scripts->didhit_script))
2703     {
2704         _execute_didhit_script(cs, ent, other, force, drop, type, noblock, guardcost, jugglecost, pauseadd, blocked, tag);
2705     }
2706 }
2707 
_execute_onspawn_script(Script * cs,entity * ent)2708 static void _execute_onspawn_script(Script *cs, entity *ent)
2709 {
2710     ScriptVariant tempvar;
2711     ScriptVariant_Init(&tempvar);
2712     ScriptVariant_ChangeType(&tempvar, VT_PTR);
2713     tempvar.ptrVal = (VOID *)ent;
2714     Script_Set_Local_Variant(cs, "self", &tempvar);
2715     Script_Execute(cs);
2716     //clear to save variant space
2717     ScriptVariant_Clear(&tempvar);
2718     Script_Set_Local_Variant(cs, "self", &tempvar);
2719 }
2720 
execute_onspawn_script(entity * ent)2721 void execute_onspawn_script(entity *ent)
2722 {
2723     Script *cs;
2724     s_scripts *gs = global_model_scripts;
2725     if(gs && (cs = gs->onspawn_script) && Script_IsInitialized(cs))
2726     {
2727         _execute_onspawn_script(cs, ent);
2728     }
2729     if(Script_IsInitialized(cs = ent->scripts->onspawn_script))
2730     {
2731         _execute_onspawn_script(cs, ent);
2732     }
2733 }
2734 
execute_onmodelcopy_script(entity * ent,entity * old)2735 void execute_onmodelcopy_script(entity *ent, entity *old)
2736 {
2737     ScriptVariant tempvar;
2738     Script *cs = ent->scripts->onmodelcopy_script;
2739     if(Script_IsInitialized(cs))
2740     {
2741         ScriptVariant_Init(&tempvar);
2742         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2743         tempvar.ptrVal = (VOID *)ent;
2744         Script_Set_Local_Variant(cs, "self", &tempvar);
2745         tempvar.ptrVal = (VOID *)old;
2746         Script_Set_Local_Variant(cs, "old", &tempvar);
2747         Script_Execute(cs);
2748         //clear to save variant space
2749         ScriptVariant_Clear(&tempvar);
2750         Script_Set_Local_Variant(cs, "self", &tempvar);
2751         Script_Set_Local_Variant(cs, "old", &tempvar);
2752     }
2753 }
2754 
execute_ondraw_script(entity * ent)2755 void execute_ondraw_script(entity *ent)
2756 {
2757     ScriptVariant tempvar;
2758     Script *cs = ent->scripts->ondraw_script;
2759     if(Script_IsInitialized(cs))
2760     {
2761         ScriptVariant_Init(&tempvar);
2762         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2763         tempvar.ptrVal = (VOID *)ent;
2764         Script_Set_Local_Variant(cs, "self", &tempvar);
2765         Script_Execute(cs);
2766         //clear to save variant space
2767         ScriptVariant_Clear(&tempvar);
2768         Script_Set_Local_Variant(cs, "self", &tempvar);
2769     }
2770 }
2771 
execute_entity_key_script(entity * ent)2772 void execute_entity_key_script(entity *ent)
2773 {
2774     ScriptVariant tempvar;
2775     Script *cs ;
2776     if(!ent)
2777     {
2778         return;
2779     }
2780     cs = ent->scripts->key_script;
2781     if(Script_IsInitialized(cs))
2782     {
2783         ScriptVariant_Init(&tempvar);
2784         ScriptVariant_ChangeType(&tempvar, VT_PTR);
2785         tempvar.ptrVal = (VOID *)ent;
2786         Script_Set_Local_Variant(cs, "self",    &tempvar);
2787         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2788         tempvar.lVal = (LONG)ent->playerindex;
2789         Script_Set_Local_Variant(cs, "player",  &tempvar);
2790         Script_Execute(cs);
2791         //clear to save variant space
2792         ScriptVariant_Clear(&tempvar);
2793         Script_Set_Local_Variant(cs, "self",    &tempvar);
2794         Script_Set_Local_Variant(cs, "player",  &tempvar);
2795     }
2796 }
2797 
execute_spawn_script(s_spawn_entry * p,entity * e)2798 void execute_spawn_script(s_spawn_entry *p, entity *e)
2799 {
2800     ScriptVariant tempvar;
2801     Script *cs;
2802     cs = &p->spawnscript;
2803     if(Script_IsInitialized(cs))
2804     {
2805         if(e)
2806         {
2807             ScriptVariant_Init(&tempvar);
2808             ScriptVariant_ChangeType(&tempvar, VT_PTR);
2809             tempvar.ptrVal = (VOID *)e;
2810             Script_Set_Local_Variant(cs, "self", &tempvar);
2811             ScriptVariant_ChangeType(&tempvar, VT_DECIMAL);
2812             tempvar.dblVal = (DOUBLE)p->position.x;
2813             Script_Set_Local_Variant(cs, "spawnx", &tempvar);
2814             tempvar.dblVal = (DOUBLE)p->position.z;
2815             Script_Set_Local_Variant(cs, "spawnz", &tempvar);
2816             tempvar.dblVal = (DOUBLE)p->position.y;
2817             Script_Set_Local_Variant(cs, "spawna", &tempvar);
2818             ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2819             tempvar.lVal = (LONG)p->at;
2820             Script_Set_Local_Variant(cs, "spawnat", &tempvar);
2821         }
2822         Script_Execute(cs);
2823         if(e)
2824         {
2825             ScriptVariant_Clear(&tempvar);
2826             Script_Set_Local_Variant(cs, "self", &tempvar);
2827             Script_Set_Local_Variant(cs, "spawnx", &tempvar);
2828             Script_Set_Local_Variant(cs, "spawnz", &tempvar);
2829             Script_Set_Local_Variant(cs, "spawna", &tempvar);
2830             Script_Set_Local_Variant(cs, "spawnat", &tempvar);
2831         }
2832     }
2833 }
2834 
execute_level_key_script(int player)2835 void execute_level_key_script(int player)
2836 {
2837     ScriptVariant tempvar;
2838     Script *cs = &(level->key_script);
2839     if(Script_IsInitialized(cs))
2840     {
2841         ScriptVariant_Init(&tempvar);
2842         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2843         tempvar.lVal = (LONG)player;
2844         Script_Set_Local_Variant(cs, "player", &tempvar);
2845         Script_Execute(cs);
2846         //clear to save variant space
2847         ScriptVariant_Clear(&tempvar);
2848         Script_Set_Local_Variant(cs, "player", &tempvar);
2849     }
2850 }
2851 
execute_key_script_all(int player)2852 void execute_key_script_all(int player)
2853 {
2854     ScriptVariant tempvar;
2855     Script *cs = &key_script_all;
2856     if(Script_IsInitialized(cs))
2857     {
2858         ScriptVariant_Init(&tempvar);
2859         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2860         tempvar.lVal = (LONG)player;
2861         Script_Set_Local_Variant(cs, "player", &tempvar);
2862         Script_Execute(cs);
2863         //clear to save variant space
2864         ScriptVariant_Clear(&tempvar);
2865         Script_Set_Local_Variant(cs, "player", &tempvar);
2866     }
2867 }
2868 
execute_timetick_script(int time,int gotime)2869 void execute_timetick_script(int time, int gotime)
2870 {
2871     ScriptVariant tempvar;
2872     Script *cs = &timetick_script;
2873     if(Script_IsInitialized(cs))
2874     {
2875         ScriptVariant_Init(&tempvar);
2876         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2877         tempvar.lVal = (LONG)time;
2878         Script_Set_Local_Variant(cs, "time",    &tempvar);
2879         tempvar.lVal = (LONG)gotime;
2880         Script_Set_Local_Variant(cs, "gotime", &tempvar);
2881         Script_Execute(cs);
2882         //clear to save variant space
2883         ScriptVariant_Clear(&tempvar);
2884         Script_Set_Local_Variant(cs, "time",    &tempvar);
2885         Script_Set_Local_Variant(cs, "gotime",  &tempvar);
2886     }
2887 }
2888 
execute_loading_script(int value,int max)2889 void execute_loading_script(int value, int max)
2890 {
2891     ScriptVariant tempvar;
2892     Script *cs = &loading_script;
2893     if(Script_IsInitialized(cs))
2894     {
2895         ScriptVariant_Init(&tempvar);
2896         ScriptVariant_ChangeType(&tempvar, VT_INTEGER);
2897         tempvar.lVal = (LONG)value;
2898         Script_Set_Local_Variant(cs, "value",    &tempvar);
2899         tempvar.lVal = (LONG)max;
2900         Script_Set_Local_Variant(cs, "max", &tempvar);
2901         Script_Execute(cs);
2902         //clear to save variant space
2903         ScriptVariant_Clear(&tempvar);
2904         Script_Set_Local_Variant(cs, "value",    &tempvar);
2905         Script_Set_Local_Variant(cs, "max",  &tempvar);
2906     }
2907 }
2908 
execute_key_script(int index)2909 void execute_key_script(int index)
2910 {
2911     if(Script_IsInitialized(&key_script[index]))
2912     {
2913         Script_Execute(&key_script[index]);
2914     }
2915 }
2916 
execute_join_script(int index)2917 void execute_join_script(int index)
2918 {
2919     if(Script_IsInitialized(&join_script[index]))
2920     {
2921         Script_Execute(&join_script[index]);
2922     }
2923 }
2924 
execute_respawn_script(int index)2925 void execute_respawn_script(int index)
2926 {
2927     if(Script_IsInitialized(&respawn_script[index]))
2928     {
2929         Script_Execute(&respawn_script[index]);
2930     }
2931 }
2932 
execute_pdie_script(int index)2933 void execute_pdie_script(int index)
2934 {
2935     if(Script_IsInitialized(&pdie_script[index]))
2936     {
2937         Script_Execute(&pdie_script[index]);
2938     }
2939 }
2940 
2941 // ------------------------ Save/load -----------------------------
2942 
clearsettings()2943 void clearsettings()
2944 {
2945     savedata.compatibleversion = COMPATIBLEVERSION;
2946     savedata.gamma = 0;
2947     savedata.brightness = 0;
2948     savedata.usesound = 1;
2949     savedata.soundrate = 44100;
2950     savedata.soundbits = 16;
2951     savedata.soundvol = 15;
2952     savedata.usemusic = 1;
2953     savedata.musicvol = 100;
2954     savedata.effectvol = 120;
2955     savedata.usejoy = 1;
2956     savedata.mode = 0;
2957     savedata.showtitles = 0;
2958     savedata.windowpos = 0;
2959     savedata.logo = 0;
2960     savedata.uselog = 1;
2961     savedata.debuginfo = 0;
2962     savedata.fullscreen = 0;
2963     savedata.stretch = 0;
2964 
2965 
2966 #ifdef SDL
2967     savedata.usegl[0] = 0;
2968     savedata.usegl[1] = 1;
2969 #ifdef ANDROID
2970     savedata.glscale = 0.0;
2971 #else
2972     savedata.glscale = 1.0;
2973 #endif
2974     savedata.glfilter[0] = 1;
2975     savedata.glfilter[1] = 0;
2976 #endif
2977 
2978     savedata.swfilter = 0;
2979 
2980 #ifdef PSP
2981     savedata.pspcpuspeed = 2;
2982     savedata.overscan[0] = 0;
2983     savedata.overscan[1] = 0;
2984     savedata.overscan[2] = 0;
2985     savedata.overscan[3] = 0;
2986 #endif
2987 
2988     savedata.keys[0][SDID_MOVEUP]    = CONTROL_DEFAULT1_UP;
2989     savedata.keys[0][SDID_MOVEDOWN]  = CONTROL_DEFAULT1_DOWN;
2990     savedata.keys[0][SDID_MOVELEFT]  = CONTROL_DEFAULT1_LEFT;
2991     savedata.keys[0][SDID_MOVERIGHT] = CONTROL_DEFAULT1_RIGHT;
2992     savedata.keys[0][SDID_ATTACK]    = CONTROL_DEFAULT1_FIRE1;
2993     savedata.keys[0][SDID_ATTACK2]   = CONTROL_DEFAULT1_FIRE2;
2994     savedata.keys[0][SDID_ATTACK3]   = CONTROL_DEFAULT1_FIRE3;
2995     savedata.keys[0][SDID_ATTACK4]   = CONTROL_DEFAULT1_FIRE4;
2996     savedata.keys[0][SDID_JUMP]      = CONTROL_DEFAULT1_FIRE5;
2997     savedata.keys[0][SDID_SPECIAL]   = CONTROL_DEFAULT1_FIRE6;
2998     savedata.keys[0][SDID_START]     = CONTROL_DEFAULT1_START;
2999     savedata.keys[0][SDID_SCREENSHOT] = CONTROL_DEFAULT1_SCREENSHOT;
3000     #ifdef SDL
3001         //savedata.keys[0][SDID_ESC]       = CONTROL_DEFAULT1_ESC;
3002     #endif
3003 
3004     savedata.keys[1][SDID_MOVEUP]    = CONTROL_DEFAULT2_UP;
3005     savedata.keys[1][SDID_MOVEDOWN]  = CONTROL_DEFAULT2_DOWN;
3006     savedata.keys[1][SDID_MOVELEFT]  = CONTROL_DEFAULT2_LEFT;
3007     savedata.keys[1][SDID_MOVERIGHT] = CONTROL_DEFAULT2_RIGHT;
3008     savedata.keys[1][SDID_ATTACK]    = CONTROL_DEFAULT2_FIRE1;
3009     savedata.keys[1][SDID_ATTACK2]   = CONTROL_DEFAULT2_FIRE2;
3010     savedata.keys[1][SDID_ATTACK3]   = CONTROL_DEFAULT2_FIRE3;
3011     savedata.keys[1][SDID_ATTACK4]   = CONTROL_DEFAULT2_FIRE4;
3012     savedata.keys[1][SDID_JUMP]      = CONTROL_DEFAULT2_FIRE5;
3013     savedata.keys[1][SDID_SPECIAL]   = CONTROL_DEFAULT2_FIRE6;
3014     savedata.keys[1][SDID_START]     = CONTROL_DEFAULT2_START;
3015     savedata.keys[1][SDID_SCREENSHOT] = CONTROL_DEFAULT2_SCREENSHOT;
3016     #ifdef SDL
3017         //savedata.keys[1][SDID_ESC]       = CONTROL_DEFAULT2_ESC;
3018     #endif
3019 
3020     savedata.keys[2][SDID_MOVEUP]    = CONTROL_DEFAULT3_UP;
3021     savedata.keys[2][SDID_MOVEDOWN]  = CONTROL_DEFAULT3_DOWN;
3022     savedata.keys[2][SDID_MOVELEFT]  = CONTROL_DEFAULT3_LEFT;
3023     savedata.keys[2][SDID_MOVERIGHT] = CONTROL_DEFAULT3_RIGHT;
3024     savedata.keys[2][SDID_ATTACK]    = CONTROL_DEFAULT3_FIRE1;
3025     savedata.keys[2][SDID_ATTACK2]   = CONTROL_DEFAULT3_FIRE2;
3026     savedata.keys[2][SDID_ATTACK3]   = CONTROL_DEFAULT3_FIRE3;
3027     savedata.keys[2][SDID_ATTACK4]   = CONTROL_DEFAULT3_FIRE4;
3028     savedata.keys[2][SDID_JUMP]      = CONTROL_DEFAULT3_FIRE5;
3029     savedata.keys[2][SDID_SPECIAL]   = CONTROL_DEFAULT3_FIRE6;
3030     savedata.keys[2][SDID_START]     = CONTROL_DEFAULT3_START;
3031     savedata.keys[2][SDID_SCREENSHOT] = CONTROL_DEFAULT3_SCREENSHOT;
3032     #ifdef SDL
3033         //savedata.keys[2][SDID_ESC]       = CONTROL_DEFAULT3_ESC;
3034     #endif
3035 
3036     savedata.keys[3][SDID_MOVEUP]    = CONTROL_DEFAULT4_UP;
3037     savedata.keys[3][SDID_MOVEDOWN]  = CONTROL_DEFAULT4_DOWN;
3038     savedata.keys[3][SDID_MOVELEFT]  = CONTROL_DEFAULT4_LEFT;
3039     savedata.keys[3][SDID_MOVERIGHT] = CONTROL_DEFAULT4_RIGHT;
3040     savedata.keys[3][SDID_ATTACK]    = CONTROL_DEFAULT4_FIRE1;
3041     savedata.keys[3][SDID_ATTACK2]   = CONTROL_DEFAULT4_FIRE2;
3042     savedata.keys[3][SDID_ATTACK3]   = CONTROL_DEFAULT4_FIRE3;
3043     savedata.keys[3][SDID_ATTACK4]   = CONTROL_DEFAULT4_FIRE4;
3044     savedata.keys[3][SDID_JUMP]      = CONTROL_DEFAULT4_FIRE5;
3045     savedata.keys[3][SDID_SPECIAL]   = CONTROL_DEFAULT4_FIRE6;
3046     savedata.keys[3][SDID_START]     = CONTROL_DEFAULT4_START;
3047     savedata.keys[3][SDID_SCREENSHOT] = CONTROL_DEFAULT4_SCREENSHOT;
3048     #ifdef SDL
3049         //savedata.keys[3][SDID_ESC]       = CONTROL_DEFAULT4_ESC;
3050     #endif
3051 }
3052 
3053 
savesettings()3054 void savesettings()
3055 {
3056 #ifndef DC
3057     FILE *handle = NULL;
3058     char path[128] = {""};
3059     char tmpname[128] = {""};
3060     getBasePath(path, "Saves", 0);
3061     getPakName(tmpname, 4);
3062     strcat(path, tmpname);
3063     handle = fopen(path, "wb");
3064     if(handle == NULL)
3065     {
3066         return;
3067     }
3068     fwrite(&savedata, 1, sizeof(savedata), handle);
3069     fclose(handle);
3070 #endif
3071 }
3072 
saveasdefault()3073 void saveasdefault()
3074 {
3075 #ifndef DC
3076     FILE *handle = NULL;
3077     char path[128] = {""};
3078     getBasePath(path, "Saves", 0);
3079     strncat(path, "default.cfg", 128);
3080     handle = fopen(path, "wb");
3081     if(handle == NULL)
3082     {
3083         return;
3084     }
3085     fwrite(&savedata, 1, sizeof(savedata), handle);
3086     fclose(handle);
3087 #endif
3088 }
3089 
3090 
loadsettings()3091 void loadsettings()
3092 {
3093 #ifndef DC
3094     FILE *handle = NULL;
3095     char path[128] = {""};
3096     char tmpname[128] = {""};
3097     getBasePath(path, "Saves", 0);
3098     getPakName(tmpname, 4);
3099     strcat(path, tmpname);
3100     if(!(fileExists(path)))
3101     {
3102         loadfromdefault();
3103         return;
3104     }
3105     clearsettings();
3106     handle = fopen(path, "rb");
3107     if(handle == NULL)
3108     {
3109         return;
3110     }
3111     fread(&savedata, 1, sizeof(savedata), handle);
3112     fclose(handle);
3113     if(savedata.compatibleversion != COMPATIBLEVERSION)
3114     {
3115         clearsettings();
3116     }
3117 #else
3118     clearsettings();
3119 #endif
3120 }
3121 
loadfromdefault()3122 void loadfromdefault()
3123 {
3124 #ifndef DC
3125     FILE *handle = NULL;
3126     char path[128] = {""};
3127     getBasePath(path, "Saves", 0);
3128     strncat(path, "default.cfg", 128);
3129     clearsettings();
3130     handle = fopen(path, "rb");
3131     if(handle == NULL)
3132     {
3133         return;
3134     }
3135     fread(&savedata, 1, sizeof(savedata), handle);
3136     fclose(handle);
3137     if(savedata.compatibleversion != COMPATIBLEVERSION)
3138     {
3139         clearsettings();
3140     }
3141 #else
3142     clearsettings();
3143 #endif
3144 }
3145 
3146 
3147 
3148 
clearSavedGame()3149 void clearSavedGame()
3150 {
3151     memset(savelevel, 0, sizeof(*savelevel)*num_difficulties);
3152 }
3153 
3154 
3155 
clearHighScore()3156 void clearHighScore()
3157 {
3158     int i;
3159     savescore.compatibleversion = CV_HIGH_SCORE;
3160     for(i = 0; i < 10; i++)
3161     {
3162         savescore.highsc[i] = 0;    // Resets all the highscores to 0
3163         strcpy(savescore.hscoren[i], "None");    // Resets all the highscore names to "None"
3164     }
3165 }
3166 
3167 
3168 
saveGameFile()3169 int saveGameFile()
3170 {
3171 #ifndef DC
3172     FILE *handle = NULL;
3173     char path[256] = {""};
3174     char tmpname[256] = {""};
3175 
3176     getBasePath(path, "Saves", 0);
3177     getPakName(tmpname, 0);
3178     strcat(path, tmpname);
3179     //if(!savelevel[saveslot].level) return;
3180     handle = fopen(path, "wb");
3181 
3182     if(handle == NULL)
3183     {
3184         return 0;
3185     }
3186 
3187     fwrite(savelevel, sizeof(*savelevel), num_difficulties, handle);
3188 
3189     fclose(handle);
3190 
3191     return 1;
3192 #else
3193     return 1;
3194 #endif
3195 }
3196 
3197 
loadGameFile()3198 int loadGameFile()
3199 {
3200 #ifndef DC
3201     int result = 1, i;
3202     FILE *handle = NULL;
3203     char path[256] = {""};
3204     char tmpname[256] = {""};
3205     //size_t filesize = 0;
3206 
3207     getBasePath(path, "Saves", 0);
3208     getPakName(tmpname, 0);
3209     strcat(path, tmpname);
3210     handle = fopen(path, "rb");
3211 
3212     if(handle == NULL)
3213     {
3214         return 0;
3215     }
3216 
3217     //fseek(handle, 0L, SEEK_END);
3218     //filesize = ftell(handle);
3219     //fseek(handle, 0L, SEEK_SET); // or rewind(handle);
3220     //(filesize != sizeof(*savelevel)*num_difficulties)
3221 
3222     if( (fread(savelevel, sizeof(*savelevel), num_difficulties, handle) >= sizeof(*savelevel) && savelevel[0].compatibleversion != CV_SAVED_GAME) )
3223     {
3224         clearSavedGame();
3225         result = 0;
3226     }
3227     else
3228     {
3229         bonus = 0;
3230         for(i = 0; i < num_difficulties; i++) if(savelevel[i].times_completed > 0)
3231             {
3232                 bonus += savelevel[i].times_completed;
3233             }
3234     }
3235 
3236     fclose(handle);
3237 
3238     return result;
3239 #else
3240     clearSavedGame();
3241     return 0;
3242 #endif
3243 }
3244 
3245 
saveHighScoreFile()3246 int saveHighScoreFile()
3247 {
3248 #ifndef DC
3249     FILE *handle = NULL;
3250     char path[256] = {""};
3251     char tmpname[256] = {""};
3252     getBasePath(path, "Saves", 0);
3253     getPakName(tmpname, 1);
3254     strcat(path, tmpname);
3255     handle = fopen(path, "wb");
3256     if(handle == NULL)
3257     {
3258         return 0;
3259     }
3260     fwrite(&savescore, 1, sizeof(savescore), handle);
3261     fclose(handle);
3262     return 1;
3263 #else
3264     return 1;
3265 #endif
3266 }
3267 
3268 
loadHighScoreFile()3269 int loadHighScoreFile()
3270 {
3271 #ifndef DC
3272     FILE *handle = NULL;
3273     char path[256] = {""};
3274     char tmpname[256] = {""};
3275     getBasePath(path, "Saves", 0);
3276     getPakName(tmpname, 1);
3277     strcat(path, tmpname);
3278     clearHighScore();
3279     handle = fopen(path, "rb");
3280     if(handle == NULL)
3281     {
3282         return 0;
3283     }
3284     fread(&savescore, 1, sizeof(savescore), handle);
3285     fclose(handle);
3286     if(savescore.compatibleversion != CV_HIGH_SCORE)
3287     {
3288         clearHighScore();
3289         return 0;
3290     }
3291     return 1;
3292 #else
3293     clearHighScore();
3294     return 0;
3295 #endif
3296 }
3297 
3298 
3299 #ifndef DC
vardump(ScriptVariant * var,char buffer[])3300 static void vardump(ScriptVariant *var, char buffer[])
3301 {
3302     char *tmpstr;
3303     int l, t, c;
3304     buffer[0] = 0;
3305     switch(var->vt)
3306     {
3307 
3308     case VT_STR:
3309         strcpy(buffer, "\"");
3310         tmpstr = StrCache_Get(var->strVal);
3311         l = strlen(tmpstr);
3312         for(c = 0; c < l; c++)
3313         {
3314             if(tmpstr[c] == '\n')
3315             {
3316                 strcat(buffer, "\\n");
3317             }
3318             else if(tmpstr[c] == '\r')
3319             {
3320                 strcat(buffer, "\\r");
3321             }
3322             else if(tmpstr[c] == '\\')
3323             {
3324                 strcat(buffer, "\\\\");
3325             }
3326             else
3327             {
3328                 t = strlen(buffer);
3329                 buffer[t] = tmpstr[c];
3330                 buffer[t + 1] = 0;
3331             }
3332         }
3333         strcat(buffer, "\"");
3334         break;
3335     case VT_DECIMAL:
3336         sprintf(buffer, "%lf", (double)var->dblVal);
3337         break;
3338     case VT_INTEGER:
3339         sprintf(buffer, "%ld", (long)var->lVal);
3340         break;
3341     default:
3342         strcpy(buffer, "NULL()");
3343         break;
3344     }
3345 }
3346 
3347 #endif
3348 
3349 
saveScriptFile()3350 int saveScriptFile()
3351 {
3352 #ifndef DC
3353 #define _writestr(v) fwrite(v, strlen(v), 1, handle);
3354 #define _writetmp  _writestr(tmpvalue)
3355 #define _writeconst(s) strcpy(tmpvalue,s);_writetmp
3356     FILE *handle = NULL;
3357     int i, l, size;
3358     ScriptVariant *var;
3359     char path[256] = {""};
3360     char tmpvalue[256] = {""};
3361     getBasePath(path, "Saves", 0);
3362     getPakName(tmpvalue, 2);//.scr
3363     strcat(path, tmpvalue);
3364     l = strlen(path); //s00, s01, s02 etc
3365     path[l - 2] = '0' + (current_set / 10);
3366     path[l - 1] = '0' + (current_set % 10);
3367     handle = fopen(path, "wb");
3368     if(handle == NULL)
3369     {
3370         return 0;
3371     }
3372 
3373     _writeconst("void main() {\n");
3374     size = List_GetSize(global_var_list.list);
3375     for(i = 0, List_Reset(global_var_list.list); i < size; List_GotoNext(global_var_list.list), i++)
3376     {
3377         var = (ScriptVariant *)List_Retrieve(global_var_list.list);
3378         if( var->vt != VT_EMPTY && var->vt != VT_PTR)
3379         {
3380             _writeconst("\tsetglobalvar(\"")
3381             _writestr(List_GetName(global_var_list.list))
3382             _writeconst("\",")
3383             vardump(var, tmpvalue);
3384             _writetmp
3385             _writeconst(");\n")
3386         }
3387     }
3388     // indexed list
3389     for(i = 1; i <= global_var_list.vars->lVal; i++)
3390     {
3391         if(global_var_list.vars[i].vt != VT_PTR && global_var_list.vars[i].vt != VT_EMPTY)
3392         {
3393             _writeconst("\tsetglobalvar(")
3394             sprintf(tmpvalue, "%d", i - 1);
3395             _writetmp
3396             _writeconst(",")
3397             vardump(global_var_list.vars + i, tmpvalue);
3398             _writetmp
3399             _writeconst(");\n")
3400         }
3401     }
3402     //allow select
3403     for(i = 0; i < models_cached; i++)
3404     {
3405         if(model_cache[i].selectable)
3406         {
3407             _writeconst("\tchangemodelproperty(\"")
3408             _writestr(model_cache[i].name)
3409             _writeconst("\",4,1);\n")
3410         }
3411         /*
3412         if(model_cache[i].model) {
3413         	_writeconst("\tloadmodel(\"")
3414         	_writestr(model_cache[i].name)
3415         	sprintf(tmpvalue, "\",%d,%d);\n", model_cache[i].model->unload, model_cache[i].selectable);
3416         	_writetmp
3417         }*/
3418     }
3419     _writeconst("}\n");
3420 
3421     fclose(handle);
3422     return 1;
3423 #undef _writestr
3424 #undef _writetmp
3425 #undef _writeconst
3426 #else
3427     return 1;
3428 #endif
3429 }
3430 
3431 
loadScriptFile()3432 int loadScriptFile()
3433 {
3434 #ifndef DC
3435     Script script;
3436     int result = 0;
3437     char *buf = NULL;
3438     ptrdiff_t l;
3439     size_t len;
3440     FILE *handle = NULL;
3441 
3442     char path[256] = {""};
3443     char tmpname[256] = {""};
3444     getBasePath(path, "Saves", 0);
3445     getPakName(tmpname, 2);//.scr
3446     strcat(path, tmpname);
3447     l = strlen(path); //s00, s01, s02 etc
3448     path[l - 2] = '0' + (current_set / 10);
3449     path[l - 1] = '0' + (current_set % 10);
3450 
3451     handle = fopen(path, "rb");
3452     if(handle == NULL)
3453     {
3454         return 0;
3455     }
3456 
3457     fseek(handle, 0, SEEK_END);
3458     len = ftell(handle);
3459     fseek(handle, 0, SEEK_SET);
3460     buf = malloc(len + 1);
3461 
3462     if(!buf)
3463     {
3464         return 0;
3465     }
3466 
3467     fread(buf, 1, len, handle);
3468     buf[len - 1] = 0;
3469 
3470     Script_Init(&script, "loadScriptFile",  NULL, 1);
3471 
3472     result = (Script_AppendText(&script, buf, path) &&
3473               Script_Compile(&script) &&
3474               Script_Execute(&script) );
3475 
3476     Script_Clear(&script, 2);
3477     free(buf);
3478     return result;
3479 #else
3480     return 0;
3481 #endif
3482 }
3483 
3484 // ----------------------- Sound ------------------------------
3485 
music(char * filename,int loop,long offset)3486 int music(char *filename, int loop, long offset)
3487 {
3488     char t[64];
3489     char a[64];
3490     int res = 1;
3491 
3492     if(!savedata.usemusic)
3493     {
3494         return 0;
3495     }
3496     if(!sound_open_music(filename, packfile, savedata.musicvol, loop, offset))
3497     {
3498         printf("\nCan't play music file '%s'\n", filename);
3499         res = 0;
3500     }
3501     if(savedata.showtitles && sound_query_music(a, t))
3502     {
3503         debug_xy_msg.font_index = 0;
3504         //debug_xy_msg.x = videomodes.hRes/2 - videomodes.hShift - (fontmonowidth(debug_xy_msg.font_index)*16);
3505         //debug_xy_msg.y = videomodes.vRes - videomodes.vShift - fontheight(debug_xy_msg.font_index);
3506         debug_xy_msg.x = fontmonowidth(debug_xy_msg.font_index);
3507         debug_xy_msg.y = videomodes.vRes - fontheight(debug_xy_msg.font_index)*2;
3508         if(a[0] && t[0])
3509         {
3510             debug_printf("Playing \"%s\" by %s", t, a);
3511         }
3512         else if(a[0])
3513         {
3514             debug_printf("Playing unknown song by %s", a);
3515         }
3516         else if(t[0])
3517         {
3518             debug_printf("Playing \"%s\" by unknown artist", t);
3519         }
3520         else
3521         {
3522             debug_printf("");
3523         }
3524     }
3525     strncpy(currentmusic, filename, sizeof(currentmusic) - 1);
3526     return res;
3527 }
3528 
check_music()3529 void check_music()
3530 {
3531     if(musicfade[1] > 0)
3532     {
3533         musicfade[1] -= musicfade[0];
3534         sound_volume_music((int)musicfade[1], (int)musicfade[1]);
3535     }
3536     else if(musicname[0])
3537     {
3538         music(musicname, musicloop, musicoffset);
3539         sound_volume_music(savedata.musicvol, savedata.musicvol);
3540         musicname[0] = 0;
3541     }
3542 }
3543 
3544 // ----------------------- General ------------------------------
3545 // atof and atoi return a valid number, if only the first char is one.
3546 // so we only check that.
isNumeric(char * text)3547 int isNumeric(char *text)
3548 {
3549     char *p = text;
3550     assert(p);
3551     if(!*p)
3552     {
3553         return 0;
3554     }
3555     switch(*p)
3556     {
3557     case '-':
3558     case '+':
3559         p++;
3560         break;
3561     default:
3562         break;
3563     }
3564     switch (*p)
3565     {
3566     case '0':
3567     case '1':
3568     case '2':
3569     case '3':
3570     case '4':
3571     case '5':
3572     case '6':
3573     case '7':
3574     case '8':
3575     case '9':
3576         return 1;
3577     default:
3578         return 0;
3579     }
3580     return 1;
3581 }
3582 
3583 
getValidInt(char * text,char * file,char * cmd)3584 int getValidInt(char *text, char *file, char *cmd)
3585 {
3586     static const char *WARN_NUMBER_EXPECTED = "WARNING: %s tries to load a nonnumeric value at %s, where a number is expected!\nerroneus string: %s\n";
3587     if(!text || !*text)
3588     {
3589         return 0;
3590     }
3591     if(isNumeric(text))
3592     {
3593         return atoi(text);
3594     }
3595     else
3596     {
3597         printf(WARN_NUMBER_EXPECTED, file, cmd, text);
3598         return 0;
3599     }
3600 
3601 }
3602 
getValidFloat(char * text,char * file,char * cmd)3603 float getValidFloat(char *text, char *file, char *cmd)
3604 {
3605     static const char *WARN_NUMBER_EXPECTED = "WARNING: %s tries to load a nonnumeric value at %s, where a number is expected!\nerroneus string: %s\n";
3606     if(!text || !*text)
3607     {
3608         return 0.0f;
3609     }
3610     if(isNumeric(text))
3611     {
3612         if(text[strlen(text) - 1] == '%')
3613         {
3614             return atof(text) / 100.0f;
3615         }
3616         return atof(text);
3617     }
3618     else
3619     {
3620         printf(WARN_NUMBER_EXPECTED, file, cmd, text);
3621         return 0.0f;
3622     }
3623 }
3624 
ParseArgs(ArgList * list,char * input,char * output)3625 size_t ParseArgs(ArgList *list, char *input, char *output)
3626 {
3627     assert(list);
3628     assert(input);
3629     assert(output);
3630     //static const char diff = 'a' - 'A';
3631 
3632     size_t pos = 0;
3633     size_t wordstart = 0;
3634     size_t item = 0;
3635     int done = 0;
3636     int space = 0;
3637     //int makelower = 0;
3638 
3639     while(pos < MAX_ARG_LEN - 1 && item < MAX_ARG_COUNT)
3640     {
3641         switch(input[pos])
3642         {
3643         case '\r':
3644         case '\n':
3645         case '#':
3646         case '\0':
3647             done = 1;
3648         case ' ':
3649         case '\t':
3650             output[pos] = '\0';
3651             if(!space && wordstart != pos)
3652             {
3653                 list->args[item] = output + wordstart;
3654                 list->arglen[item] = pos - wordstart;
3655                 item++;
3656             }
3657             space = 1;
3658             break; /*
3659 			case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
3660 			case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
3661 			case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
3662 				makelower = 1; */
3663 
3664         default:
3665             if(space)
3666             {
3667                 wordstart = pos;
3668             }
3669             /*output[pos] = makelower ? input[pos] + diff : input[pos];*/
3670             output[pos] = input[pos];
3671             space = 0;
3672             //makelower = 0;
3673         }
3674         if(done)
3675         {
3676             break;
3677         }
3678         pos++;
3679     }
3680     list->count = item;
3681     return item;
3682 }
3683 
readByte(char * buf)3684 int readByte(char *buf)
3685 {
3686     int num = 0;
3687 
3688     num = (unsigned int)buf[0]&0xFF;
3689 
3690     return num;
3691 }
3692 
findarg(char * command,int which)3693 char *findarg(char *command, int which)
3694 {
3695     static const char comment_mark[] = {"#"};
3696     int d;
3697     int argc;
3698     int inarg;
3699     int argstart;
3700     static char arg[MAX_ARG_LEN];
3701 
3702 
3703     // Copy the command line, replacing spaces by zeroes,
3704     // finally returning a pointer to the requested arg.
3705     d = 0;
3706     inarg = 0;
3707     argstart = 0;
3708     argc = -1;
3709 
3710     while(d < MAX_ARG_LEN - 1 && command[d])
3711     {
3712         // Zero out whitespace
3713         if(command[d] == ' ' || command[d] == '\t')
3714         {
3715             arg[d] = 0;
3716             inarg = 0;
3717             if(argc == which)
3718             {
3719                 return arg + argstart;
3720             }
3721         }
3722         else if(command[d] == 0 || command[d] == '\n' || command[d] == '\r' ||
3723                 strcmp(command + d, comment_mark) == 0)
3724         {
3725             // End of line
3726             arg[d] = 0;
3727             if(argc == which)
3728             {
3729                 return arg + argstart;
3730             }
3731             return arg + d;
3732         }
3733         else
3734         {
3735             if(!inarg)
3736             {
3737                 // if(argc==-1 && command[d]=='#') return arg;
3738                 inarg = 1;
3739                 argstart = d;
3740                 argc++;
3741             }
3742             arg[d] = command[d];
3743         }
3744         ++d;
3745     }
3746     arg[d] = 0;
3747 
3748     return arg;
3749 }
3750 
3751 
3752 
3753 
diff(float a,float b)3754 float diff(float a, float b)
3755 {
3756     if(a < b)
3757     {
3758         return b - a;
3759     }
3760     return a - b;
3761 }
3762 
3763 
3764 
inair(entity * e)3765 int inair(entity *e)
3766 {
3767     return (diff(e->position.y, e->base) >= 0.1);
3768 }
3769 
inair_range(entity * e)3770 int inair_range(entity *e)
3771 {
3772     return (diff(e->position.y, e->base) > T_WALKOFF);
3773 }
3774 
3775 
3776 // ----------------------- Loaders ------------------------------
3777 
3778 
3779 // Creates a remapping table from two images
load_colourmap(s_model * model,char * image1,char * image2)3780 int load_colourmap(s_model *model, char *image1, char *image2)
3781 {
3782     int i, j, k;
3783     unsigned char *map = NULL;
3784     s_bitmap *bitmap1 = NULL;
3785     s_bitmap *bitmap2 = NULL;
3786 
3787     // Can't use same image twice!
3788     if(stricmp(image1, image2) == 0)
3789     {
3790         return 0;
3791     }
3792 
3793     __realloc(model->colourmap, model->maps_loaded);
3794     k = model->maps_loaded++;
3795 
3796     if((map = malloc(MAX_PAL_SIZE / 4)) == NULL)
3797     {
3798         return -2;
3799     }
3800     if((bitmap1 = loadbitmap(image1, packfile, PIXEL_8)) == NULL)
3801     {
3802         free(map);
3803         map = NULL;
3804         return -3;
3805     }
3806     if((bitmap2 = loadbitmap(image2, packfile, PIXEL_8)) == NULL)
3807     {
3808         freebitmap(bitmap1);
3809         free(map);
3810         map = NULL;
3811         return -4;
3812     }
3813 
3814     // Create the colour map
3815     for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3816     {
3817         map[i] = i;
3818     }
3819     for(j = 0; j < bitmap1->height && j < bitmap2->height; j++)
3820     {
3821         for(i = 0; i < bitmap1->width && i < bitmap2->width; i++)
3822         {
3823             map[(unsigned)(bitmap1->data[j * bitmap1->width + i])] = bitmap2->data[j * bitmap2->width + i];
3824         }
3825     }
3826 
3827     freebitmap(bitmap1);
3828     freebitmap(bitmap2);
3829 
3830     model->colourmap[k] = map;
3831     return 1;
3832 }
3833 
3834 //PIXEL_x8
3835 // This function is used to enable remap command in 24bit mode
3836 // So old mods can still run under 16/24/32bit color system
3837 // This function should be called when all colourmaps are loaded, e.g.,
3838 // at the end of load_cached_model
3839 // map flag is used to determine whether a colourmap is a real colourmap
convert_map_to_palette(s_model * model,unsigned mapflag[])3840 int convert_map_to_palette(s_model *model, unsigned mapflag[])
3841 {
3842     int i, c;
3843     unsigned char *newmap, *oldmap;
3844     unsigned char *p1, *p2;
3845     unsigned pb = pixelbytes[(int)screenformat];
3846     if(model->palette == NULL)
3847     {
3848         return 0;
3849     }
3850     for(c = 0; c < model->maps_loaded; c++)
3851     {
3852         if(mapflag[c] == 0)
3853         {
3854             continue;
3855         }
3856         if((newmap = malloc(PAL_BYTES)) == NULL)
3857         {
3858             shutdown(1, "Error convert_map_to_palette for model: %s\n", model->name);
3859         }
3860         // Create new colour map
3861         memcpy(newmap, model->palette, PAL_BYTES);
3862         oldmap = model->colourmap[c];
3863         for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3864         {
3865             if(oldmap[i] == i)
3866             {
3867                 continue;
3868             }
3869             p1 = newmap + i * pb;
3870             p2 = model->palette + oldmap[i] * pb;
3871             memcpy(p1, p2, pb);
3872         }
3873         model->colourmap[c] = newmap;
3874         free(oldmap);
3875         oldmap = NULL;
3876     }
3877     return 1;
3878 }
3879 
_load_palette16(unsigned char * palette,char * filename)3880 static int _load_palette16(unsigned char *palette, char *filename)
3881 {
3882     int handle, i;
3883     unsigned char tp[3];
3884     handle = openpackfile(filename, packfile);
3885     if(handle < 0)
3886     {
3887         return 0;
3888     }
3889     memset(palette, 0, MAX_PAL_SIZE / 2);
3890     for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3891     {
3892         if(readpackfile(handle, tp, 3) != 3)
3893         {
3894             closepackfile(handle);
3895             return 0;
3896         }
3897         ((unsigned short *)palette)[i] = colour16(tp[0], tp[1], tp[2]);
3898     }
3899     closepackfile(handle);
3900     *(unsigned short *)palette = 0;
3901 
3902     return 1;
3903 }
3904 
3905 
_load_palette32(unsigned char * palette,char * filename)3906 static int _load_palette32(unsigned char *palette, char *filename)
3907 {
3908     int handle, i;
3909     unsigned *dp;
3910     unsigned char tpal[3];
3911     handle = openpackfile(filename, packfile);
3912     if(handle < 0)
3913     {
3914         return 0;
3915     }
3916     memset(palette, 0, MAX_PAL_SIZE);
3917     dp = (unsigned *)palette;
3918     for(i = 0; i < MAX_PAL_SIZE / 4; i++)
3919     {
3920         if(readpackfile(handle, tpal, 3) != 3)
3921         {
3922             closepackfile(handle);
3923             return 0;
3924         }
3925         dp[i] = colour32(tpal[0], tpal[1], tpal[2]);
3926 
3927     }
3928     closepackfile(handle);
3929     dp[0] = 0;
3930 
3931     return 1;
3932 }
3933 
3934 //load a 256 colors' palette
load_palette(unsigned char * palette,char * filename)3935 int load_palette(unsigned char *palette, char *filename)
3936 {
3937     int handle;
3938     if(screenformat == PIXEL_32)
3939     {
3940         return _load_palette32(palette, filename);
3941     }
3942     else if(screenformat == PIXEL_16)
3943     {
3944         return _load_palette16(palette, filename);
3945     }
3946 
3947     handle = openpackfile(filename, packfile);
3948     if(handle < 0)
3949     {
3950         return 0;
3951     }
3952     if(readpackfile(handle, palette, 768) != 768)
3953     {
3954         closepackfile(handle);
3955         return 0;
3956     }
3957     closepackfile(handle);
3958     palette[0] = palette[1] = palette[2] = 0;
3959 
3960     return 1;
3961 }
3962 
3963 // create blending tables for the palette
create_blending_tables(unsigned char * palette,unsigned char * tables[],int usemap[])3964 int create_blending_tables(unsigned char *palette, unsigned char *tables[], int usemap[])
3965 {
3966     int i;
3967     if(pixelformat != PIXEL_8)
3968     {
3969         return 1;
3970     }
3971     if(!palette || !tables)
3972     {
3973         return 0;
3974     }
3975 
3976     memset(tables, 0, MAX_BLENDINGS * sizeof(*tables));
3977     for(i = 0; i < MAX_BLENDINGS; i++)
3978     {
3979         if(!usemap || usemap[i])
3980         {
3981             tables[i] = (blending_table_functions[i])(palette);
3982             if(!tables[i])
3983             {
3984                 return 0;
3985             }
3986         }
3987     }
3988 
3989     return 1;
3990 }
3991 
create_blend_tables_x8(unsigned char * tables[])3992 void create_blend_tables_x8(unsigned char *tables[])
3993 {
3994     int i;
3995     for(i = 0; i < MAX_BLENDINGS; i++)
3996     {
3997         switch(screenformat)
3998         {
3999         case PIXEL_16:
4000             tables[i] = blending_table_functions16[i] ? (blending_table_functions16[i])() : NULL;
4001             break;
4002         case PIXEL_32:
4003             tables[i] = blending_table_functions32[i] ? (blending_table_functions32[i])() : NULL;
4004             break;
4005         }
4006     }
4007 
4008 }
4009 
4010 
4011 //change system palette by index
change_system_palette(int palindex)4012 void change_system_palette(int palindex)
4013 {
4014     if(palindex < 0)
4015     {
4016         palindex = 0;
4017     }
4018     //if(current_palette == palindex ) return;
4019 
4020 
4021     if(!level || palindex == 0 || palindex > level->numpalettes)
4022     {
4023         current_palette = 0;
4024         if(screenformat == PIXEL_8)
4025         {
4026             palette_set_corrected(pal, savedata.gamma, savedata.gamma, savedata.gamma, savedata.brightness, savedata.brightness, savedata.brightness);
4027             set_blendtables(blendings); // set global blending tables
4028         }
4029     }
4030     else if(level)
4031     {
4032         current_palette = palindex;
4033         if(screenformat == PIXEL_8)
4034         {
4035             palette_set_corrected(level->palettes[palindex - 1], savedata.gamma, savedata.gamma, savedata.gamma, savedata.brightness, savedata.brightness, savedata.brightness);
4036             set_blendtables(level->blendings[palindex - 1]);
4037         }
4038     }
4039 }
4040 
4041 // Load colour 0-127 from data/pal.act
standard_palette(int immediate)4042 void standard_palette(int immediate)
4043 {
4044     unsigned char pp[MAX_PAL_SIZE] = {0};
4045     if(load_palette(pp, "data/pal.act"))
4046     {
4047         memcpy(pal, pp, (PAL_BYTES) / 2);
4048     }
4049     if(immediate)
4050     {
4051         change_system_palette(0);
4052     }
4053 }
4054 
4055 
unload_background()4056 void unload_background()
4057 {
4058     int i;
4059     if (background)
4060     {
4061         clearscreen(background);
4062     }
4063     for(i = 0; i < MAX_BLENDINGS; i++)
4064     {
4065         if(blendings[i])
4066         {
4067             free(blendings[i]);
4068         }
4069         blendings[i] = NULL;
4070     }
4071 }
4072 
4073 
_makecolour(int r,int g,int b)4074 int _makecolour(int r, int g, int b)
4075 {
4076     switch(screenformat)
4077     {
4078     case PIXEL_8:
4079         return palette_find(pal, r, g, b);
4080     case PIXEL_16:
4081         return colour16(r, g, b);
4082     case PIXEL_32:
4083         return colour32(r, g, b);
4084     }
4085     return 0;
4086 }
4087 
4088 // parses a color string in the format "R_G_B" or as a raw integer
parsecolor(const char * string)4089 int parsecolor(const char *string)
4090 {
4091     int r, g, b;
4092     if(strchr(string, '_') != strrchr(string, '_'))
4093     {
4094         // 2 underscores; color is in "R_G_B" format
4095         r = atoi(string);
4096         g = atoi(strchr(string, '_') + 1);
4097         b = atoi(strrchr(string, '_') + 1);
4098         return _makecolour(r, g, b);
4099     }
4100     else
4101     {
4102         return atoi(string);    // raw integer
4103     }
4104 }
4105 
4106 // ltb 1-17-05   new function for lifebar colors
lifebar_colors()4107 void lifebar_colors()
4108 {
4109     char *filename = "data/lifebar.txt";
4110     char *buf;
4111     size_t size;
4112     int pos;
4113     ArgList arglist;
4114     char argbuf[MAX_ARG_LEN + 1] = "";
4115 
4116 
4117     char *command;
4118 
4119     if(buffer_pakfile(filename, &buf, &size) != 1)
4120     {
4121         color_black = 0;
4122         color_red = 0;
4123         color_orange = 0;
4124         color_yellow = 0;
4125         color_white = 0;
4126         color_blue = 0;
4127         color_green = 0;
4128         color_pink = 0;
4129         color_purple = 0;
4130         color_magic = 0;
4131         color_magic2 = 0;
4132         shadowcolor = 0;
4133         shadowalpha = BLEND_MULTIPLY + 1;
4134         shadowopacity = 255;
4135         return;
4136     }
4137 
4138     pos = 0;
4139     colorbars = 1;
4140     while(pos < size)
4141     {
4142         if(ParseArgs(&arglist, buf + pos, argbuf))
4143         {
4144             command = GET_ARG(0);
4145             if(command && command[0])
4146             {
4147                 if(stricmp(command, "blackbox") == 0)
4148                 {
4149                     color_black = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4150                 }
4151                 else if(stricmp(command, "whitebox") == 0)
4152                 {
4153                     color_white = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4154                 }
4155                 else if(stricmp(command, "color300") == 0)
4156                 {
4157                     color_orange = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4158                 }
4159                 else if(stricmp(command, "color25") == 0)
4160                 {
4161                     color_red = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4162                 }
4163                 else if(stricmp(command, "color50") == 0)
4164                 {
4165                     color_yellow = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4166                 }
4167                 else if(stricmp(command, "color100") == 0)
4168                 {
4169                     color_green = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4170                 }
4171                 else if(stricmp(command, "color200") == 0)
4172                 {
4173                     color_blue = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4174                 }
4175                 else if(stricmp(command, "color400") == 0)
4176                 {
4177                     color_pink = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4178                 }
4179                 else if(stricmp(command, "color500") == 0)
4180                 {
4181                     color_purple = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4182                 }
4183                 //magic bars color declarations by tails
4184                 else if(stricmp(command, "colormagic") == 0)
4185                 {
4186                     color_magic = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4187                 }
4188                 else if(stricmp(command, "colormagic2") == 0)
4189                 {
4190                     color_magic2 = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4191                 }
4192                 //end of magic bars color declarations by tails
4193                 else if(stricmp(command, "shadowcolor") == 0)
4194                 {
4195                     shadowcolor = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
4196                 }
4197                 else if(stricmp(command, "shadowalpha") == 0) //gfxshadow alpha
4198                 {
4199                     shadowalpha = GET_INT_ARG(1);
4200                 }
4201                 else if(stricmp(command, "shadowopacity") == 0)
4202                 {
4203                     shadowopacity = GET_INT_ARG(1);
4204                 }
4205                 else if(command && command[0])
4206                 {
4207                     printf("Warning: Unknown command in lifebar.txt: '%s'.\n", command);
4208                 }
4209             }
4210         }
4211 
4212         // Go to next line
4213         pos += getNewLineStart(buf + pos);
4214     }
4215     if(buf != NULL)
4216     {
4217         free(buf);
4218         buf = NULL;
4219     }
4220 }
4221 // ltb 1-17-05 end new lifebar colors
4222 
4223 
init_colourtable()4224 void init_colourtable()
4225 {
4226     mpcolourtable[0]  = color_magic2;
4227     mpcolourtable[1]  = color_magic;
4228     mpcolourtable[2]  = color_magic;
4229     mpcolourtable[3]  = color_magic;
4230     mpcolourtable[4]  = color_magic2;
4231     mpcolourtable[5]  = color_magic;
4232     mpcolourtable[6]  = color_magic2;
4233     mpcolourtable[7]  = color_magic;
4234     mpcolourtable[8]  = color_magic2;
4235     mpcolourtable[9]  = color_magic;
4236     mpcolourtable[10] = color_magic2;
4237 
4238     hpcolourtable[0]  = color_purple;
4239     hpcolourtable[1]  = color_red;
4240     hpcolourtable[2]  = color_yellow;
4241     hpcolourtable[3]  = color_green;
4242     hpcolourtable[4]  = color_blue;
4243     hpcolourtable[5]  = color_orange;
4244     hpcolourtable[6]  = color_pink;
4245     hpcolourtable[7]  = color_purple;
4246     hpcolourtable[8]  = color_black;
4247     hpcolourtable[9]  = color_white;
4248     hpcolourtable[10] = color_white;
4249 
4250     memcpy(ldcolourtable, hpcolourtable, 11 * sizeof(*hpcolourtable));
4251 }
4252 
load_background(char * filename,int createtables)4253 void load_background(char *filename, int createtables)
4254 {
4255     //if(pixelformat!=PIXEL_8) createtables = 0;
4256     unload_background();
4257 
4258     if(pixelformat == PIXEL_8)
4259     {
4260         if(!loadscreen(filename, packfile, pal, PIXEL_8, &background))
4261         {
4262             shutdown(1, "Error loading background (PIXEL_8) file '%s'", filename);
4263         }
4264     }
4265     else if(pixelformat == PIXEL_x8)
4266     {
4267         if(!loadscreen(filename, packfile, NULL, PIXEL_x8, &background))
4268         {
4269             if (screenformat == PIXEL_32)
4270             {
4271                 if (loadscreen32(filename, packfile, &background)) printf("Loaded 32-bit background '%s'\n", filename);
4272                 else shutdown(1, "Error loading background (PIXEL_x8/PIXEL_32) file '%s'", filename);
4273             }
4274             else shutdown(1, "Error loading background (PIXEL_x8) file '%s'", filename);
4275         }
4276         if (background->pixelformat == PIXEL_x8)
4277         {
4278             memcpy(pal, background->palette, PAL_BYTES);
4279             memcpy(neontable, pal, PAL_BYTES);
4280         }
4281     }
4282     else
4283     {
4284         shutdown(1, "Error loading background, Unknown Pixel Format!\n");
4285     }
4286 
4287     if(createtables)
4288     {
4289         standard_palette(0);
4290         if(!create_blending_tables(pal, blendings, blendfx))
4291         {
4292             shutdown(1, "Failed to create colour conversion tables! (Out of memory?)");
4293         }
4294     }
4295 
4296     lifebar_colors();
4297     if(!color_black)
4298     {
4299         color_black = _makecolour(0, 0, 0);    // black boxes 500-600HP
4300     }
4301     if(!color_red)
4302     {
4303         color_red = _makecolour(255, 0, 0);    // 1% - 25% Full Health
4304     }
4305     if(!color_orange)
4306     {
4307         color_orange = _makecolour(255, 150, 0);    // 200-300HP
4308     }
4309     if(!color_yellow)
4310     {
4311         color_yellow = _makecolour(0xF8, 0xB8, 0x40);    // 26%-50% Full health
4312     }
4313     if(!color_white)
4314     {
4315         color_white = _makecolour(255, 255, 255);    // white boxes 600+ HP
4316     }
4317     if(!color_blue)
4318     {
4319         color_blue = _makecolour(0, 0, 255);    // 100-200 HP
4320     }
4321     if(!color_green)
4322     {
4323         color_green = _makecolour(0, 255, 0);    // 51% - 100% full health
4324     }
4325     if(!color_pink)
4326     {
4327         color_pink = _makecolour(255, 0, 255);    // 300-400HP
4328     }
4329     if(!color_purple)
4330     {
4331         color_purple = _makecolour(128, 48, 208);    // transbox 400-500HP
4332     }
4333     if(!color_magic)
4334     {
4335         color_magic = _makecolour(98, 180, 255);    // 1st magic bar color by tails
4336     }
4337     if(!color_magic2)
4338     {
4339         color_magic2 = _makecolour(24, 48, 143);    // 2sec magic bar color by tails
4340     }
4341     if(!shadowcolor)
4342     {
4343         shadowcolor =  _makecolour(64, 64, 64);
4344     }
4345     init_colourtable();
4346 
4347     video_clearscreen();
4348     pal[0] = pal[1] = pal[2] = 0;
4349     //palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
4350     change_system_palette(0);
4351 }
4352 
load_cached_background(char * filename,int createtables)4353 void load_cached_background(char *filename, int createtables)
4354 {
4355 #ifndef CACHE_BACKGROUNDS
4356     load_background(filename, createtables);
4357 #else
4358     int index = -1;
4359     unload_background();
4360 
4361     if(strcmp(filename, "data/bgs/logo") == 0)
4362     {
4363         index = 0;
4364     }
4365     else if(strcmp(filename, "data/bgs/title") == 0)
4366     {
4367         index = 1;
4368     }
4369     else if(strcmp(filename, "data/bgs/titleb") == 0)
4370     {
4371         index = 2;
4372     }
4373     else if(strcmp(filename, "data/bgs/loading") == 0)
4374     {
4375         index = 3;
4376     }
4377     else if(strcmp(filename, "data/bgs/loading2") == 0)
4378     {
4379         index = 4;
4380     }
4381     else if(strcmp(filename, "data/bgs/hiscore") == 0)
4382     {
4383         index = 5;
4384     }
4385     else if(strcmp(filename, "data/bgs/complete") == 0)
4386     {
4387         index = 6;
4388     }
4389     else if(strcmp(filename, "data/bgs/unlockbg") == 0)
4390     {
4391         index = 7;
4392     }
4393     else if(strcmp(filename, "data/bgs/select") == 0)
4394     {
4395         index = 8;
4396     }
4397 
4398     if((index == -1) || (bg_cache[index] == NULL))
4399     {
4400         shutdown(1, "Error: can't load cached background '%s'", filename);
4401     }
4402 
4403     if(background)
4404     {
4405         freescreen(&background);
4406     }
4407     background = allocscreen(videomodes.hRes, videomodes.vRes, bg_cache[index]->pixelformat);
4408     copyscreen(background, bg_cache[index]);
4409 
4410     if(background->pixelformat == PIXEL_8)
4411     {
4412         memcpy(pal, bg_palette_cache[index], PAL_BYTES);
4413     }
4414     else if(background->pixelformat == PIXEL_x8)
4415     {
4416         memcpy(background->palette, bg_cache[index]->palette, PAL_BYTES);
4417         memcpy(pal, background->palette, PAL_BYTES);
4418     }
4419 
4420 
4421     if(createtables)
4422     {
4423         standard_palette(0);
4424         if(!create_blending_tables(pal, blendings, blendfx))
4425         {
4426             shutdown(1, "Failed to create colour conversion tables! (Out of memory?)");
4427         }
4428     }
4429 
4430     video_clearscreen();
4431     pal[0] = pal[1] = pal[2] = 0;
4432     //palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
4433     change_system_palette(0);
4434     printf("use cached bg\n");
4435 #endif
4436 }
4437 
4438 #ifdef CACHE_BACKGROUNDS
cache_background(char * filename)4439 void cache_background(char *filename)
4440 {
4441     s_screen *bg = allocscreen(videomodes.hRes, videomodes.vRes, pixelformat);
4442     int index = -1;
4443 
4444     if(pixelformat == PIXEL_8)
4445     {
4446         if(!loadscreen(filename, packfile, pal, pixelformat, &bg))
4447         {
4448             freescreen(&bg);
4449             bg = NULL;
4450         }
4451     }
4452     else if(pixelformat == PIXEL_x8)
4453     {
4454         if(!loadscreen(filename, packfile, NULL, pixelformat, &bg))
4455         {
4456             if (!(screenformat == PIXEL_32 && loadscreen32(filename, packfile, &bg)))
4457             {
4458                 freescreen(&bg);
4459                 bg = NULL;
4460             }
4461         }
4462     }
4463     else
4464     {
4465         shutdown(1, "Error caching background, Unknown Pixel Format!\n");
4466     }
4467 
4468     if(strcmp(filename, "data/bgs/logo") == 0)
4469     {
4470         index = 0;
4471     }
4472     else if(strcmp(filename, "data/bgs/title") == 0)
4473     {
4474         index = 1;
4475     }
4476     else if(strcmp(filename, "data/bgs/titleb") == 0)
4477     {
4478         index = 2;
4479     }
4480     else if(strcmp(filename, "data/bgs/loading") == 0)
4481     {
4482         index = 3;
4483     }
4484     else if(strcmp(filename, "data/bgs/loading2") == 0)
4485     {
4486         index = 4;
4487     }
4488     else if(strcmp(filename, "data/bgs/hiscore") == 0)
4489     {
4490         index = 5;
4491     }
4492     else if(strcmp(filename, "data/bgs/complete") == 0)
4493     {
4494         index = 6;
4495     }
4496     else if(strcmp(filename, "data/bgs/unlockbg") == 0)
4497     {
4498         index = 7;
4499     }
4500     else if(strcmp(filename, "data/bgs/select") == 0)
4501     {
4502         index = 8;
4503     }
4504     else
4505     {
4506         shutdown(1, "Error: unknown cached background '%s'", filename);
4507     }
4508 
4509     bg_cache[index] = bg;
4510 
4511     if(pixelformat == PIXEL_8)
4512     {
4513         memcpy(bg_palette_cache[index], pal, PAL_BYTES);
4514     }
4515 
4516     change_system_palette(0);
4517 }
4518 
cache_all_backgrounds()4519 void cache_all_backgrounds()
4520 {
4521     cache_background("data/bgs/logo");
4522     cache_background("data/bgs/title");
4523     cache_background("data/bgs/titleb");
4524     cache_background("data/bgs/loading2");
4525     cache_background("data/bgs/hiscore");
4526     cache_background("data/bgs/complete");
4527     cache_background("data/bgs/unlockbg");
4528     cache_background("data/bgs/select");
4529 }
4530 #endif
4531 
load_layer(char * filename,char * maskfilename,int index)4532 void load_layer(char *filename, char *maskfilename, int index)
4533 {
4534     if(!level)
4535     {
4536         return;
4537     }
4538 
4539     if(filename && level->layers[index].gfx.handle == NULL)
4540     {
4541         if(*maskfilename || ((level->layers[index].drawmethod.alpha > 0 || level->layers[index].drawmethod.transbg) && !level->layers[index].drawmethod.water.watermode))
4542         {
4543             // assume sprites are faster than screen when transparency or alpha are specified
4544             level->layers[index].gfx.sprite = loadsprite2(filename, &(level->layers[index].size.x), &(level->layers[index].size.y));
4545             if (*maskfilename)
4546             {
4547                 level->layers[index].gfx.sprite->mask = loadsprite2(maskfilename, &(level->layers[index].size.x), &(level->layers[index].size.y));
4548                 *maskfilename = 0; // clear mask filename so mask is only used for this one sprite
4549             }
4550         }
4551         else
4552         {
4553             // use screen for water effect for now, it should be faster than sprite
4554             // otherwise, a screen should be fine, especially in 8bit mode, it is super fast,
4555             //            or, at least it is not slower than a sprite
4556             if(loadscreen(filename, packfile, NULL, pixelformat, &level->layers[index].gfx.screen))
4557             {
4558                 level->layers[index].size.y = level->layers[index].gfx.screen->height;
4559                 level->layers[index].size.x = level->layers[index].gfx.screen->width;
4560             }
4561         }
4562     }
4563 
4564     if(filename && level->layers[index].gfx.handle == NULL)
4565     {
4566         shutdown(1, "Error loading file '%s'", filename);
4567     }
4568     else
4569     {
4570         if(level->layers[index].drawmethod.xrepeat < 0)
4571         {
4572             level->layers[index].offset.x -= level->layers[index].size.x * 20000;
4573             level->layers[index].drawmethod.xrepeat = 40000;
4574         }
4575         if(level->layers[index].drawmethod.yrepeat < 0)
4576         {
4577             level->layers[index].offset.z -= level->layers[index].size.y * 20000;
4578             level->layers[index].drawmethod.yrepeat = 40000;
4579         }
4580         //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);
4581     }
4582 
4583 }
4584 
4585 
loadsprite2(char * filename,int * width,int * height)4586 s_sprite *loadsprite2(char *filename, int *width, int *height)
4587 {
4588     size_t size;
4589     s_bitmap *bitmap = NULL;
4590     s_sprite *sprite = NULL;
4591     int clipl, clipr, clipt, clipb;
4592 
4593     bitmap = loadbitmap(filename, packfile, pixelformat);
4594     if(!bitmap)
4595     {
4596         return NULL;
4597     }
4598     if(width)
4599     {
4600         *width = bitmap->width;
4601     }
4602     if(height)
4603     {
4604         *height = bitmap->height;
4605     }
4606     clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
4607     size = fakey_encodesprite(bitmap);
4608     sprite = (s_sprite *)malloc(size);
4609     if(!sprite)
4610     {
4611         freebitmap(bitmap);
4612         return NULL;
4613     }
4614     encodesprite(-clipl, -clipt, bitmap, sprite);
4615     sprite->offsetx = clipl;
4616     sprite->offsety = clipt;
4617     sprite->srcwidth = bitmap->width;
4618     sprite->srcheight = bitmap->height;
4619     freebitmap(bitmap);
4620 
4621     return sprite;
4622 }
4623 
4624 
4625 // Added to conserve memory
resourceCleanUp()4626 void resourceCleanUp()
4627 {
4628     freesprites();
4629     free_models();
4630     free_modelcache();
4631     load_special_sounds();
4632     load_script_setting();
4633     load_special_sprites();
4634     load_levelorder();
4635     load_models();
4636 }
4637 
freesprites()4638 void freesprites()
4639 {
4640     unsigned i;
4641     s_sprite_list *head;
4642     for(i = 0; i <= sprites_loaded; i++)
4643     {
4644         if(sprite_list != NULL)
4645         {
4646             free(sprite_list->sprite);
4647             sprite_list->sprite = NULL;
4648             free(sprite_list->filename);
4649             sprite_list->filename = NULL;
4650             head = sprite_list->next;
4651             free(sprite_list);
4652             sprite_list = head;
4653         }
4654     }
4655     if(sprite_map != NULL)
4656     {
4657         free(sprite_map);
4658         sprite_map = NULL;
4659     }
4660     sprites_loaded = 0;
4661 }
4662 
4663 // allocate enough members for sprite_map
prepare_sprite_map(size_t size)4664 void prepare_sprite_map(size_t size)
4665 {
4666     if(sprite_map == NULL || size + 1 > sprite_map_max_items )
4667     {
4668 #ifdef VERBOSE
4669         printf("%s %p\n", "prepare_sprite_map was", sprite_map);
4670 #endif
4671         sprite_map_max_items = (((size + 1) >> 8) + 1) << 8;
4672         sprite_map = realloc(sprite_map, sizeof(*sprite_map) * sprite_map_max_items);
4673         if(sprite_map == NULL)
4674         {
4675             shutdown(1, "Out Of Memory!  Failed to create a new sprite_map\n");
4676         }
4677     }
4678 }
4679 
cachesound(int index,int load)4680 void cachesound(int index, int load)
4681 {
4682     if(index < 0)
4683     {
4684         return;
4685     }
4686     if(load)
4687     {
4688         sound_reload_sample(index);
4689     }
4690     else
4691     {
4692         sound_unload_sample(index);
4693     }
4694 }
4695 
cachesprite(int index,int load)4696 void cachesprite(int index, int load)
4697 {
4698     if(sprite_map && index >= 0 && index < sprites_loaded)
4699     {
4700         if(!load && sprite_map[index].node->sprite)
4701         {
4702             free(sprite_map[index].node->sprite);
4703             sprite_map[index].node->sprite = NULL;
4704             //printf("uncached sprite: %s\n", sprite_map[index].node->filename);
4705         }
4706         else if(load && !sprite_map[index].node->sprite)
4707         {
4708             sprite_map[index].node->sprite = loadsprite2(sprite_map[index].node->filename, NULL, NULL);
4709         }
4710     }
4711 }
4712 
4713 // Returns sprite index.
4714 // Does not return on error, as it would shut the program down.
4715 // UT:
4716 // bmpformat - In 24bit mode, a sprite can have a 24bit palette(e.g., panel),
4717 //             so add this paramter to let sprite encoding function know.
4718 //             Actually the sprite pixel encoding method is the same, but a
4719 //             24bit palettte sprite should have a palette allocated at the end of
4720 //             pixel data, and the information is carried by the bitmap paramter.
loadsprite(char * filename,int ofsx,int ofsy,int bmpformat)4721 int loadsprite(char *filename, int ofsx, int ofsy, int bmpformat)
4722 {
4723     ptrdiff_t i, size, len;
4724     s_bitmap *bitmap = NULL;
4725     int clipl, clipr, clipt, clipb;
4726     s_sprite_list *curr = NULL, *head = NULL, *toshare = NULL;
4727 
4728     for(i = 0; i < sprites_loaded; i++)
4729     {
4730         if(sprite_map && sprite_map[i].node)
4731         {
4732             if(stricmp(sprite_map[i].node->filename, filename) == 0)
4733             {
4734                 if(!sprite_map[i].node->sprite)
4735                 {
4736                     sprite_map[i].node->sprite = loadsprite2(filename, NULL, NULL);
4737                 }
4738                 if(sprite_map[i].centerx + sprite_map[i].node->sprite->offsetx == ofsx &&
4739                         sprite_map[i].centery + sprite_map[i].node->sprite->offsety == ofsy)
4740                 {
4741                     return i;
4742                 }
4743                 else
4744                 {
4745                     toshare = sprite_map[i].node;
4746                 }
4747             }
4748         }
4749     }
4750 
4751     if(toshare)
4752     {
4753         prepare_sprite_map(sprites_loaded + 1);
4754         sprite_map[sprites_loaded].node = toshare;
4755         sprite_map[sprites_loaded].centerx = ofsx - toshare->sprite->offsetx;
4756         sprite_map[sprites_loaded].centery = ofsy - toshare->sprite->offsety;
4757         ++sprites_loaded;
4758         return sprites_loaded - 1;
4759     }
4760 
4761     bitmap = loadbitmap(filename, packfile, bmpformat);
4762     if(bitmap == NULL)
4763     {
4764         shutdown(1, "Unable to load file '%s'\n", filename);
4765     }
4766 
4767     clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
4768 
4769     len = strlen(filename);
4770     size = fakey_encodesprite(bitmap);
4771     curr = malloc(sizeof(*curr));
4772     curr->sprite = malloc(size);
4773     curr->filename = malloc(len + 1);
4774     if(curr == NULL || curr->sprite == NULL || curr->filename == NULL)
4775     {
4776         freebitmap(bitmap);
4777         shutdown(1, "loadsprite() Out of memory!\n");
4778     }
4779     memcpy(curr->filename, filename, len);
4780     curr->filename[len] = 0;
4781     encodesprite(ofsx - clipl, ofsy - clipt, bitmap, curr->sprite);
4782     if(sprite_list == NULL)
4783     {
4784         sprite_list = curr;
4785         sprite_list->next = NULL;
4786     }
4787     else
4788     {
4789         head = sprite_list;
4790         sprite_list = curr;
4791         sprite_list->next = head;
4792     }
4793     prepare_sprite_map(sprites_loaded + 1);
4794     sprite_map[sprites_loaded].node = sprite_list;
4795     sprite_map[sprites_loaded].centerx = ofsx - clipl;
4796     sprite_map[sprites_loaded].centery = ofsy - clipt;
4797     sprite_list->sprite->offsetx = clipl;
4798     sprite_list->sprite->offsety = clipt;
4799     sprite_list->sprite->srcwidth = bitmap->width;
4800     sprite_list->sprite->srcheight = bitmap->height;
4801     freebitmap(bitmap);
4802     ++sprites_loaded;
4803     return sprites_loaded - 1;
4804 }
4805 
load_special_sprites()4806 void load_special_sprites()
4807 {
4808     memset(shadowsprites, -1, sizeof(*shadowsprites) * 6);
4809     golsprite = gosprite = -1;
4810     if(testpackfile("data/sprites/shadow1.gif", packfile) >= 0)
4811     {
4812         shadowsprites[0] = loadsprite("data/sprites/shadow1", 9, 3, pixelformat);
4813     }
4814     if(testpackfile("data/sprites/shadow2.gif", packfile) >= 0)
4815     {
4816         shadowsprites[1] = loadsprite("data/sprites/shadow2", 14, 5, pixelformat);
4817     }
4818     if(testpackfile("data/sprites/shadow3.gif", packfile) >= 0)
4819     {
4820         shadowsprites[2] = loadsprite("data/sprites/shadow3", 19, 6, pixelformat);
4821     }
4822     if(testpackfile("data/sprites/shadow4.gif", packfile) >= 0)
4823     {
4824         shadowsprites[3] = loadsprite("data/sprites/shadow4", 24, 8, pixelformat);
4825     }
4826     if(testpackfile("data/sprites/shadow5.gif", packfile) >= 0)
4827     {
4828         shadowsprites[4] = loadsprite("data/sprites/shadow5", 29, 9, pixelformat);
4829     }
4830     if(testpackfile("data/sprites/shadow6.gif", packfile) >= 0)
4831     {
4832         shadowsprites[5] = loadsprite("data/sprites/shadow6", 34, 11, pixelformat);
4833     }
4834     if(testpackfile("data/sprites/arrow.gif", packfile) >= 0)
4835     {
4836         gosprite  = loadsprite("data/sprites/arrow", 35, 23, pixelformat);
4837     }
4838     if(testpackfile("data/sprites/arrowl.gif", packfile) >= 0)
4839     {
4840         golsprite = loadsprite("data/sprites/arrowl", 35, 23, pixelformat);
4841     }
4842     if(timeicon_path[0])
4843     {
4844         timeicon = loadsprite(timeicon_path, 0, 0, pixelformat);
4845     }
4846     if(bgicon_path[0])
4847     {
4848         bgicon = loadsprite(bgicon_path, 0, 0, pixelformat);
4849     }
4850     if(olicon_path[0])
4851     {
4852         olicon = loadsprite(olicon_path, 0, 0, pixelformat);
4853     }
4854 }
4855 
unload_all_fonts()4856 void unload_all_fonts()
4857 {
4858     int i;
4859     for(i = 0; i < MAX_FONTS; i++)
4860     {
4861         font_unload(i);
4862     }
4863 }
4864 
load_all_fonts()4865 void load_all_fonts()
4866 {
4867     char path[256];
4868     int i;
4869 
4870     for(i = 0; i < MAX_FONTS; i++)
4871     {
4872         if(i == 0)
4873         {
4874             strcpy(path, "data/sprites/font");
4875         }
4876         else
4877         {
4878             sprintf(path, "%s%d", "data/sprites/font", i + 1);
4879         }
4880         if(font_load(i, path, packfile, fontmonospace[i] | fontmbs[i]))
4881         {
4882             // Plombo 3/1/2013: allow fonts to have alpha masks
4883             if(i == 0)
4884             {
4885                 strcpy(path, "data/sprites/fontmask");
4886             }
4887             else
4888             {
4889                 sprintf(path, "%s%d", "data/sprites/fontmask", i + 1);
4890             }
4891             if(font_loadmask(i, path, packfile, fontmonospace[i] | fontmbs[i]))
4892             {
4893                 printf("%d(m) ", i + 1);
4894             }
4895             else
4896             {
4897                 printf("%d ", i + 1);
4898             }
4899         }
4900     }
4901 }
4902 
translate_SDID(char * value)4903 int translate_SDID(char *value)
4904 {
4905     if(stricmp(value, "moveup") == 0)
4906     {
4907         return SDID_MOVEUP;
4908     }
4909     else if(stricmp(value, "movedown") == 0)
4910     {
4911         return SDID_MOVEDOWN;
4912     }
4913     else if(stricmp(value, "moveleft") == 0)
4914     {
4915         return SDID_MOVELEFT;
4916     }
4917     else if(stricmp(value, "moveright") == 0)
4918     {
4919         return SDID_MOVERIGHT;
4920     }
4921     else if(stricmp(value, "attack") == 0)
4922     {
4923         return SDID_ATTACK;
4924     }
4925     else if(stricmp(value, "attack2") == 0)
4926     {
4927         return SDID_ATTACK2;
4928     }
4929     else if(stricmp(value, "attack3") == 0)
4930     {
4931         return SDID_ATTACK3;
4932     }
4933     else if(stricmp(value, "attack4") == 0)
4934     {
4935         return SDID_ATTACK4;
4936     }
4937     else if(stricmp(value, "jump") == 0)
4938     {
4939         return SDID_JUMP;
4940     }
4941     else if(stricmp(value, "special") == 0)
4942     {
4943         return SDID_SPECIAL;
4944     }
4945     else if(stricmp(value, "start") == 0)
4946     {
4947         return SDID_START;
4948     }
4949     else if(stricmp(value, "screenshot") == 0)
4950     {
4951         return SDID_SCREENSHOT;
4952     }
4953     else if(stricmp(value, "esc") == 0)
4954     {
4955         return SDID_ESC;
4956     }
4957 
4958     return -1;
4959 }
4960 
load_menu_txt()4961 void load_menu_txt()
4962 {
4963     char *filename = "data/menu.txt";
4964     int pos, i;
4965     char *buf, *command;
4966     size_t size;
4967     ArgList arglist;
4968     char argbuf[MAX_ARG_LEN + 1] = "";
4969 
4970     // Read file
4971     if(buffer_pakfile(filename, &buf, &size) != 1)
4972     {
4973         return;
4974     }
4975 
4976     // Now interpret the contents of buf line by line
4977     pos = 0;
4978     while(pos < size)
4979     {
4980         if(ParseArgs(&arglist, buf + pos, argbuf))
4981         {
4982             command = GET_ARG(0);
4983             if(command && command[0])
4984             {
4985                 if(stricmp(command, "fontmonospace") == 0)
4986                 {
4987                     for(i = 0; i < MAX_FONTS; i++)
4988                     {
4989                         fontmonospace[i] = GET_INT_ARG((i + 1)) ? FONT_MONO : 0;
4990                     }
4991                 }
4992                 else if(stricmp(command, "fontmbs") == 0)
4993                 {
4994                     for(i = 0; i < MAX_FONTS; i++)
4995                     {
4996                         fontmbs[i] = GET_INT_ARG((i + 1)) ? FONT_MBS : 0;
4997                     }
4998                 }
4999             }
5000         }
5001 
5002         // Go to next line
5003         pos += getNewLineStart(buf + pos);
5004     }
5005 
5006     if(buf != NULL)
5007     {
5008         free(buf);
5009         buf = NULL;
5010     }
5011 }
5012 
load_special_sounds()5013 int load_special_sounds()
5014 {
5015     sound_unload_all_samples();
5016     SAMPLE_GO		= sound_load_sample("data/sounds/go.wav",		packfile,	0);
5017     SAMPLE_BEAT		= sound_load_sample("data/sounds/beat1.wav",	packfile,	0);
5018     SAMPLE_BLOCK	= sound_load_sample("data/sounds/block.wav",	packfile,	0);
5019     SAMPLE_FALL		= sound_load_sample("data/sounds/fall.wav",		packfile,	0);
5020     SAMPLE_GET		= sound_load_sample("data/sounds/get.wav",		packfile,	0);
5021     SAMPLE_GET2		= sound_load_sample("data/sounds/money.wav",	packfile,	0);
5022     SAMPLE_JUMP		= sound_load_sample("data/sounds/jump.wav",		packfile,	0);
5023     SAMPLE_INDIRECT	= sound_load_sample("data/sounds/indirect.wav",	packfile,	0);
5024     SAMPLE_PUNCH	= sound_load_sample("data/sounds/punch.wav",	packfile,	0);
5025     SAMPLE_1UP		= sound_load_sample("data/sounds/1up.wav",		packfile,	0);
5026     SAMPLE_TIMEOVER = sound_load_sample("data/sounds/timeover.wav", packfile,	0);
5027     SAMPLE_BEEP		= sound_load_sample("data/sounds/beep.wav",		packfile,	0);
5028     SAMPLE_BEEP2	= sound_load_sample("data/sounds/beep2.wav",	packfile,	0);
5029     SAMPLE_PAUSE	= sound_load_sample("data/sounds/pause.wav",	packfile,	0);
5030     SAMPLE_BIKE		= sound_load_sample("data/sounds/bike.wav",		packfile,	0);
5031 
5032     if ( SAMPLE_PAUSE < 0 ) SAMPLE_PAUSE = SAMPLE_BEEP2;
5033     if(SAMPLE_GO < 0 || SAMPLE_BEAT < 0 || SAMPLE_BLOCK < 0 ||
5034             SAMPLE_FALL < 0 || SAMPLE_GET < 0 || SAMPLE_GET2 < 0 ||
5035             SAMPLE_JUMP < 0 || SAMPLE_INDIRECT < 0 || SAMPLE_PUNCH < 0 ||
5036             SAMPLE_1UP < 0 || SAMPLE_TIMEOVER < 0 || SAMPLE_BEEP < 0 ||
5037             SAMPLE_BEEP2 < 0 || SAMPLE_PAUSE < 0 || SAMPLE_BIKE < 0)
5038     {
5039         return 0;
5040     }
5041     return 1;
5042 }
5043 
nextcolourmap(s_model * model,int c)5044 static int nextcolourmap(s_model *model, int c)
5045 {
5046     do
5047     {
5048         c++;
5049         if(c > model->maps_loaded)
5050         {
5051             c = 0;
5052         }
5053     }
5054     while(    // Keep looping until a non frozen map is found
5055         (model->maps.frozen > 0 && c == model->maps.frozen) ||
5056         (model->maps.hide_start > 0 && c >= model->maps.hide_start && c <= model->maps.hide_end)
5057     );
5058 
5059     return c;
5060 }
5061 
prevcolourmap(s_model * model,int c)5062 static int prevcolourmap(s_model *model, int c)
5063 {
5064     do
5065     {
5066         c--;
5067         if(c < 0)
5068         {
5069             c = model->maps_loaded;
5070         }
5071     }
5072     while(    // Keep looping until a non frozen map is found
5073         (model->maps.frozen > 0 && c == model->maps.frozen) ||
5074         (model->maps.hide_start > 0 && c >= model->maps.hide_start && c <= model->maps.hide_end)
5075     );
5076 
5077     return c;
5078 }
5079 
5080 // Use by player select menus
nextplayermodel(s_model * current)5081 s_model *nextplayermodel(s_model *current)
5082 {
5083     int i;
5084     int curindex = -1;
5085     int loops;
5086     if(current)
5087     {
5088         // Find index of current player model
5089         for(i = 0; i < models_cached; i++)
5090         {
5091             if(model_cache[i].model == current)
5092             {
5093                 curindex = i;
5094                 break;
5095             }
5096         }
5097     }
5098     // Find next player model (first one after current index)
5099     for(i = curindex + 1, loops = 0; loops < models_cached; i++, loops++)
5100     {
5101         if(i >= models_cached)
5102         {
5103             i = 0;
5104         }
5105         if(model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER &&
5106                 (allow_secret_chars || !model_cache[i].model->secret) &&
5107                 model_cache[i].model->clearcount <= bonus && model_cache[i].selectable)
5108         {
5109             //printf("next %s\n", model_cache[i].model->name);
5110             return model_cache[i].model;
5111         }
5112     }
5113     shutdown(1, "Fatal: can't find any player models!");
5114     return NULL;
5115 }
5116 
nextplayermodeln(s_model * current,int p)5117 s_model *nextplayermodeln(s_model *current, int p)
5118 {
5119     int i;
5120     s_set_entry *set = levelsets + current_set;
5121     s_model *model = nextplayermodel(current);
5122     if(set->nosame & 1)
5123     {
5124         for(i = 0; model && i < set->maxplayers; i++)
5125         {
5126             if(i != p && stricmp(model->name, player[i].name) == 0)
5127             {
5128                 model = nextplayermodel(model);
5129             }
5130         }
5131     }
5132     return model;
5133 }
5134 
5135 // Use by player select menus
prevplayermodel(s_model * current)5136 s_model *prevplayermodel(s_model *current)
5137 {
5138     int i;
5139     int curindex = -1;
5140     int loops;
5141     if(current)
5142     {
5143         // Find index of current player model
5144         for(i = 0; i < models_cached; i++)
5145         {
5146             if(model_cache[i].model == current)
5147             {
5148                 curindex = i;
5149                 break;
5150             }
5151         }
5152     }
5153     // Find next player model (first one after current index)
5154     for(i = curindex - 1, loops = 0; loops < models_cached; i--, loops++)
5155     {
5156         if(i < 0)
5157         {
5158             i = models_cached - 1;
5159         }
5160         if(model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER &&
5161                 (allow_secret_chars || !model_cache[i].model->secret) &&
5162                 model_cache[i].model->clearcount <= bonus && model_cache[i].selectable)
5163         {
5164             //printf("prev %s\n", model_cache[i].model->name);
5165             return model_cache[i].model;
5166         }
5167     }
5168     shutdown(1, "Fatal: can't find any player models!");
5169     return NULL;
5170 }
5171 
prevplayermodeln(s_model * current,int p)5172 s_model *prevplayermodeln(s_model *current, int p)
5173 {
5174     int i;
5175     s_set_entry *set = levelsets + current_set;
5176     s_model *model = prevplayermodel(current);
5177     if(set->nosame & 1)
5178     {
5179         for(i = 0; model && i < set->maxplayers; i++)
5180         {
5181             if(i != p && stricmp(model->name, player[i].name) == 0)
5182             {
5183                 model = prevplayermodel(model);
5184             }
5185         }
5186     }
5187     return model;
5188 }
5189 
5190 // Reset All Player Models to on/off for Select Screen.
reset_playable_list(char which)5191 static void reset_playable_list(char which)
5192 {
5193     int i;
5194     for(i = 0; i < models_cached; i++)
5195     {
5196         if(!which || (model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER))
5197         {
5198             model_cache[i].selectable = which;
5199         }
5200     }
5201 }
5202 
5203 // Specify which Player Models are allowable for selecting
load_playable_list(char * buf)5204 static void load_playable_list(char *buf)
5205 {
5206     int i, index;
5207     char *value;
5208     s_model *playermodels = NULL;
5209     ArgList arglist;
5210     char argbuf[MAX_ALLOWSELECT_LEN] = "";
5211 
5212     ParseArgs(&arglist, buf, argbuf);
5213 
5214     // avoid to load characters if there isn't an allowselect
5215     if ( stricmp(value = GET_ARG(0), "allowselect") != 0 ) return;
5216 
5217     reset_playable_list(0);
5218 
5219     for(i = 0; i < sizeof(argbuf); i++) allowselect_args[i] = ' ';
5220     for(i = 0; i < sizeof(argbuf); i++)
5221     {
5222         if ( argbuf[i] != '\0' ) allowselect_args[i] = argbuf[i]; // store allowselect players for savefile
5223         else allowselect_args[i] = ' ';
5224     }
5225     allowselect_args[sizeof(argbuf)-1] = '\0';
5226 
5227     for(i = 1; (value = GET_ARG(i))[0]; i++)
5228     {
5229         playermodels = findmodel(value);
5230         //if(playermodels == NULL) shutdown(1, "Player model '%s' is not loaded.\n", value);
5231         index = get_cached_model_index(playermodels->name);
5232         if(index == -1)
5233         {
5234             shutdown(1, "Player model '%s' is not cached.\n", value);
5235         }
5236         model_cache[index].selectable = 1;
5237     }
5238 
5239     return;
5240 }
5241 
alloc_specials(s_model * newchar)5242 void alloc_specials(s_model *newchar)
5243 {
5244     newchar->special = realloc(newchar->special, sizeof(*newchar->special) * (newchar->specials_loaded + 1));
5245     memset(newchar->special + newchar->specials_loaded, 0, sizeof(*newchar->special));
5246 }
5247 
alloc_frames(s_anim * anim,int fcount)5248 void alloc_frames(s_anim *anim, int fcount)
5249 {
5250     anim->sprite = malloc(fcount * sizeof(*anim->sprite));
5251     anim->delay = malloc(fcount * sizeof(*anim->delay));
5252     anim->vulnerable = malloc(fcount * sizeof(*anim->vulnerable));
5253     memset(anim->sprite, 0, fcount * sizeof(*anim->sprite));
5254     memset(anim->delay, 0, fcount * sizeof(*anim->delay));
5255     memset(anim->vulnerable, 0, fcount * sizeof(*anim->vulnerable));
5256 }
5257 
free_frames(s_anim * anim)5258 void free_frames(s_anim *anim)
5259 {
5260     int i, instance;
5261 
5262     if(anim->offset)
5263     {
5264         for(i = 0; i < anim->numframes; i++)
5265         {
5266             if(anim->offset[i])
5267             {
5268                 free(anim->offset[i]);
5269                 anim->offset[i] = NULL;
5270             }
5271         }
5272         free(anim->offset);
5273         anim->offset = NULL;
5274     }
5275 
5276     if(anim->idle)
5277     {
5278         free(anim->idle);
5279         anim->idle = NULL;
5280     }
5281 
5282     if(anim->move)
5283     {
5284         for(i = 0; i < anim->numframes; i++)
5285         {
5286             if(anim->move[i])
5287             {
5288                 free(anim->move[i]);
5289                 anim->move[i] = NULL;
5290             }
5291         }
5292         free(anim->move);
5293         anim->move = NULL;
5294     }
5295 
5296     if(anim->delay)
5297     {
5298         free(anim->delay);
5299         anim->delay = NULL;
5300     }
5301     if(anim->sprite)
5302     {
5303         free(anim->sprite);
5304         anim->sprite = NULL;
5305     }
5306     if(anim->platform)
5307     {
5308         free(anim->platform);
5309         anim->platform = NULL;
5310     }
5311     if(anim->vulnerable)
5312     {
5313         free(anim->vulnerable);
5314         anim->vulnerable = NULL;
5315     }
5316     if(anim->collision_body)
5317     {
5318         for(i = 0; i < anim->numframes; i++)
5319         {
5320             if(anim->collision_body[i])
5321             {
5322                 // Check each instance and free memory as needed.
5323                 // Momma always said put your toys away when you're done!
5324                 for(instance = 0; instance < max_collisons; instance++)
5325                 {
5326                     if(anim->collision_body[i]->instance[instance])
5327                     {
5328                         // First free any pointers allocated
5329                         // for sub structures.
5330 
5331                         // Coords.
5332                         if(anim->collision_body[i]->instance[instance]->coords)
5333                         {
5334                             free(anim->collision_body[i]->instance[instance]->coords);
5335                             anim->collision_body[i]->instance[instance]->coords = NULL;
5336                         }
5337 
5338                         free(anim->collision_body[i]->instance[instance]);
5339                         anim->collision_body[i]->instance[instance] = NULL;
5340                     }
5341                 }
5342 
5343                 free(anim->collision_body[i]);
5344                 anim->collision_body[i] = NULL;
5345             }
5346         }
5347         free(anim->collision_body);
5348         anim->collision_body = NULL;
5349     }
5350     if(anim->shadow)
5351     {
5352         free(anim->shadow);
5353         anim->shadow = NULL;
5354     }
5355     if(anim->shadow_coords)
5356     {
5357         free(anim->shadow_coords);
5358         anim->shadow_coords = NULL;
5359     }
5360     if(anim->soundtoplay)
5361     {
5362         free(anim->soundtoplay);
5363         anim->soundtoplay = NULL;
5364     }
5365     if(anim->collision_attack)
5366     {
5367         for(i = 0; i < anim->numframes; i++)
5368         {
5369             if(anim->collision_attack[i])
5370             {
5371                 // Check each attack instance and free memory as needed.
5372                 // Momma always said put your toys away when you're done!
5373                 for(instance = 0; instance < max_collisons; instance++)
5374                 {
5375                     if(anim->collision_attack[i]->instance[instance])
5376                     {
5377                         // First free any pointers allocated
5378                         // for sub structures.
5379 
5380                         // Coords.
5381                         if(anim->collision_attack[i]->instance[instance]->coords)
5382                         {
5383                             free(anim->collision_attack[i]->instance[instance]->coords);
5384                             anim->collision_attack[i]->instance[instance]->coords = NULL;
5385                         }
5386 
5387                         // Recursive damage.
5388                         if(anim->collision_attack[i]->instance[instance]->recursive)
5389                         {
5390                             free(anim->collision_attack[i]->instance[instance]->recursive);
5391                             anim->collision_attack[i]->instance[instance]->recursive = NULL;
5392                         }
5393 
5394                         free(anim->collision_attack[i]->instance[instance]);
5395                         anim->collision_attack[i]->instance[instance] = NULL;
5396                     }
5397                 }
5398 
5399                 free(anim->collision_attack[i]);
5400                 anim->collision_attack[i] = NULL;
5401             }
5402         }
5403         free(anim->collision_attack);
5404         anim->collision_attack = NULL;
5405     }
5406     if(anim->drawmethods)
5407     {
5408         for(i = 0; i < anim->numframes; i++)
5409         {
5410             if(anim->drawmethods[i])
5411             {
5412                 free(anim->drawmethods[i]);
5413                 anim->drawmethods[i] = NULL;
5414             }
5415         }
5416         free(anim->drawmethods);
5417         anim->drawmethods = NULL;
5418     }
5419 }
5420 
5421 #if 0
5422 s_anim_list *anim_list_delete(s_anim_list *list, int index)
5423 {
5424     if(list == NULL)
5425     {
5426         return NULL;
5427     }
5428     if(list->anim->model_index == index)
5429     {
5430         s_anim_list *next;
5431         next = list->next;
5432         free_anim(list->anim);
5433         free(list);
5434         --anims_loaded;
5435         return next;
5436     }
5437     list->next = anim_list_delete(list->next, index);
5438     return list;
5439 }
5440 #endif
5441 
anim_list_delete(int index)5442 void anim_list_delete(int index)
5443 {
5444     s_anim_list head;
5445     head.next = anim_list;
5446     s_anim_list *list = &head;
5447     while(list && list->next)
5448     {
5449         if(list->next->anim->model_index == index)
5450         {
5451             s_anim_list *next = list->next->next;
5452             free_anim(list->next->anim);
5453             if(list->next == anim_list)
5454             {
5455                 anim_list = next;
5456             }
5457             free(list->next);
5458             --anims_loaded;
5459             list->next = next;
5460         }
5461         else
5462         {
5463             list = list->next;
5464         }
5465     }
5466 }
5467 
free_anim(s_anim * anim)5468 void free_anim(s_anim *anim)
5469 {
5470     if(!anim)
5471     {
5472         return;
5473     }
5474     free_frames(anim);
5475     if(anim->weaponframe)
5476     {
5477         free(anim->weaponframe);
5478         anim->weaponframe = NULL;
5479     }
5480     if(anim->spawnframe)
5481     {
5482         free(anim->spawnframe);
5483         anim->spawnframe = NULL;
5484     }
5485     if(anim->summonframe)
5486     {
5487         free(anim->summonframe);
5488         anim->summonframe = NULL;
5489     }
5490     if(anim->counterrange)
5491     {
5492         free(anim->counterrange);
5493         anim->counterrange = NULL;
5494     }
5495     if(anim->dropframe)
5496     {
5497         free(anim->dropframe);
5498         anim->dropframe = NULL;
5499     }
5500     if(anim->energycost)
5501     {
5502         free(anim->energycost);
5503         anim->energycost = NULL;
5504     }
5505     free(anim);
5506 }
5507 
hasFreetype(s_model * m,e_ModelFreetype t)5508 int hasFreetype(s_model *m, e_ModelFreetype t)
5509 {
5510     assert(m);
5511     return (m->freetypes & t) == t;
5512 }
5513 
addFreeType(s_model * m,e_ModelFreetype t)5514 void addFreeType(s_model *m, e_ModelFreetype t)
5515 {
5516     assert(m);
5517     m->freetypes |= t;
5518 }
5519 
cache_model_sprites(s_model * m,int ld)5520 void cache_model_sprites(s_model *m, int ld)
5521 {
5522     int i, f, instance;
5523     s_anim *anim;
5524     cachesprite(m->icon.def, ld);
5525     cachesprite(m->icon.die, ld);
5526     cachesprite(m->icon.get, ld);
5527     cachesprite(m->icon.mphigh, ld);
5528     cachesprite(m->icon.mplow, ld);
5529     cachesprite(m->icon.mpmed, ld);
5530     cachesprite(m->icon.pain, ld);
5531     cachesprite(m->icon.weapon, ld);
5532     cachesound(m->diesound, ld);
5533     for(i = 0; i < MAX_PLAYERS; i++)
5534     {
5535         cachesprite(m->parrow[i][0], ld);
5536     }
5537 
5538     //if(hasFreetype(model, MF_ANIMLIST)){
5539     for(i = 0; i < max_animations; i++)
5540     {
5541         anim = m->animation[i];
5542         if(anim)
5543         {
5544             for(f = 0; f < anim->numframes; f++)
5545             {
5546                 cachesprite(anim->sprite[f], ld);
5547                 if(anim->soundtoplay)
5548                 {
5549                     cachesound(anim->soundtoplay[f], ld);
5550                 }
5551                 if(anim->collision_attack && anim->collision_attack[f])
5552                 {
5553                     for(instance = 0; instance < max_collisons; instance++)
5554                     {
5555                         cachesound(anim->collision_attack[f]->instance[instance]->hitsound, ld);
5556                         cachesound(anim->collision_attack[f]->instance[instance]->blocksound, ld);
5557                     }
5558                 }
5559             }
5560         }
5561     }
5562 }
5563 
5564 // Unload single model from memory
free_model(s_model * model)5565 int free_model(s_model *model)
5566 {
5567     int i;
5568     if(!model)
5569     {
5570         return 0;
5571     }
5572     printf("Unload '%s' ", model->name);
5573 
5574     if(hasFreetype(model, MF_ANIMLIST))
5575     {
5576         anim_list_delete(model->index);
5577     }
5578 
5579     printf(".");
5580 
5581     if(hasFreetype(model, MF_COLOURMAP))
5582     {
5583         for(i = 0; i < model->maps_loaded; i++)
5584         {
5585             if(model->colourmap[i] != NULL)
5586             {
5587                 free(model->colourmap[i]);
5588                 model->colourmap[i] = NULL;
5589             }
5590         }
5591         if(model->colourmap)
5592         {
5593             free(model->colourmap);
5594         }
5595         model->colourmap = NULL;
5596         model->maps_loaded = 0;
5597     }
5598 
5599     printf(".");
5600 
5601     if(hasFreetype(model, MF_PALETTE) && model->palette)
5602     {
5603         free(model->palette);
5604         model->palette = NULL;
5605     }
5606     printf(".");
5607     if(hasFreetype(model, MF_WEAPONS) && model->weapon && model->ownweapons)
5608     {
5609         free(model->weapon);
5610         model->weapon = NULL;
5611     }
5612     printf(".");
5613     if(hasFreetype(model, MF_BRANCH) && model->branch)
5614     {
5615         free(model->branch);
5616         model->branch = NULL;
5617     }
5618     printf(".");
5619     if(hasFreetype(model, MF_ANIMATION) && model->animation)
5620     {
5621         free(model->animation);
5622         model->animation = NULL;
5623     }
5624     printf(".");
5625     if(hasFreetype(model, MF_DEFENSE) && model->defense)
5626     {
5627         free(model->defense);
5628         model->defense = NULL;
5629     }
5630     printf(".");
5631     if(hasFreetype(model, MF_OFF_FACTORS) && model->offense_factors)
5632     {
5633         free(model->offense_factors);
5634         model->offense_factors = NULL;
5635     }
5636     printf(".");
5637     if(hasFreetype(model, MF_SPECIAL) && model->special)
5638     {
5639         free(model->special);
5640         model->special = NULL;
5641     }
5642     printf(".");
5643     if(hasFreetype(model, MF_SMARTBOMB) && model->smartbomb)
5644     {
5645         free(model->smartbomb);
5646         model->smartbomb = NULL;
5647     }
5648     printf(".");
5649 
5650     if(hasFreetype(model, MF_SCRIPTS))
5651     {
5652         clear_all_scripts(model->scripts, 2);
5653         free_all_scripts(&model->scripts);
5654     }
5655     printf(".");
5656 
5657     model_cache[model->index].model = NULL;
5658     deleteModel(model->name);
5659     printf(".");
5660 
5661     printf("Done.\n");
5662 
5663     return models_loaded--;
5664 }
5665 
5666 // Unload all models and animations memory
free_models()5667 void free_models()
5668 {
5669     s_model *temp;
5670 
5671     while((temp = getFirstModel()))
5672     {
5673         free_model(temp);
5674     }
5675 
5676     // free animation ids
5677     if(animdowns)
5678     {
5679         free(animdowns);
5680         animdowns          = NULL;
5681     }
5682     if(animups)
5683     {
5684         free(animups);
5685         animups            = NULL;
5686     }
5687     if(animbackwalks)
5688     {
5689         free(animbackwalks);
5690         animbackwalks      = NULL;
5691     }
5692     if(animwalks)
5693     {
5694         free(animwalks);
5695         animwalks          = NULL;
5696     }
5697     if(animidles)
5698     {
5699         free(animidles);
5700         animidles          = NULL;
5701     }
5702     if(animspecials)
5703     {
5704         free(animspecials);
5705         animspecials       = NULL;
5706     }
5707     if(animattacks)
5708     {
5709         free(animattacks);
5710         animattacks        = NULL;
5711     }
5712     if(animfollows)
5713     {
5714         free(animfollows);
5715         animfollows        = NULL;
5716     }
5717     if(animpains)
5718     {
5719         free(animpains);
5720         animpains          = NULL;
5721     }
5722     if(animbackpains)
5723     {
5724         free(animbackpains);
5725         animbackpains      = NULL;
5726     }
5727     if(animfalls)
5728     {
5729         free(animfalls);
5730         animfalls          = NULL;
5731     }
5732     if(animbackfalls)
5733     {
5734         free(animbackfalls);
5735         animbackfalls      = NULL;
5736     }
5737     if(animrises)
5738     {
5739         free(animrises);
5740         animrises          = NULL;
5741     }
5742     if(animriseattacks)
5743     {
5744         free(animriseattacks);
5745         animriseattacks    = NULL;
5746     }
5747     if(animblkpains)
5748     {
5749         free(animblkpains);
5750         animblkpains       = NULL;
5751     }
5752     if(animdies)
5753     {
5754         free(animdies);
5755         animdies           = NULL;
5756     }
5757     if(animbackdies)
5758     {
5759         free(animbackdies);
5760         animbackdies        = NULL;
5761     }
5762 }
5763 
5764 
alloc_anim()5765 s_anim *alloc_anim()
5766 {
5767     static int animindex = 0;
5768     s_anim_list *curr = NULL, *head = NULL;
5769     curr = malloc(sizeof(*curr));
5770     curr->anim = malloc(sizeof(*curr->anim));
5771     if(curr == NULL || curr->anim == NULL)
5772     {
5773         return NULL;
5774     }
5775     memset(curr->anim, 0, sizeof(*curr->anim));
5776     curr->anim->index = animindex++;
5777     if(anim_list == NULL)
5778     {
5779         anim_list = curr;
5780         anim_list->next = NULL;
5781     }
5782     else
5783     {
5784         head = anim_list;
5785         anim_list = curr;
5786         anim_list->next = head;
5787     }
5788     ++anims_loaded;
5789     return anim_list->anim;
5790 }
5791 
5792 // Caskey, Damon V.
5793 // 2016-11-27
5794 //
5795 // Allocate a collision attack instance, copy
5796 // property data if present, and return pointer.
collision_alloc_attack_instance(s_collision_attack * properties)5797 s_collision_attack *collision_alloc_attack_instance(s_collision_attack *properties)
5798 {
5799     s_collision_attack  *result;
5800     size_t              alloc_size;
5801 
5802     // Get amount of memory we'll need.
5803     alloc_size = sizeof(*result);
5804 
5805     // Allocate memory and get pointer.
5806     result = malloc(alloc_size);
5807 
5808     // If previous data is provided,
5809     // copy into new allocation.
5810     if(properties)
5811     {
5812         memcpy(result, properties, alloc_size);
5813     }
5814 
5815     // return result.
5816     return result;
5817 }
5818 
5819 // Caskey, Damon V.
5820 // 2016-11-27
5821 //
5822 // Allocate an empty collision attack list.
collision_alloc_attack_list()5823 s_collision_attack **collision_alloc_attack_list()
5824 {
5825     s_collision_attack **result;
5826     size_t             alloc_size;
5827 
5828     // Get amount of memory we'll need.
5829     alloc_size = max_collisons * sizeof(*result);
5830 
5831     // Allocate memory and get pointer.
5832     result = malloc(alloc_size);
5833 
5834     // Make sure the list is blank.
5835     memset(result, 0, alloc_size);
5836 
5837     // return result.
5838     return result;
5839 }
5840 
5841 // Caskey, Damon V.
5842 // 2016-11-27
5843 //
5844 // Allocate a collision body instance, copy
5845 // property data if present, and return pointer.
collision_alloc_body_instance(s_collision_body * properties)5846 s_collision_body *collision_alloc_body_instance(s_collision_body *properties)
5847 {
5848     s_collision_body    *result;
5849     size_t              alloc_size;
5850 
5851     // Get amount of memory we'll need.
5852     alloc_size = sizeof(*result);
5853 
5854     // Allocate memory and get pointer.
5855     result = malloc(alloc_size);
5856 
5857     // If previous data is provided,
5858     // copy into new allocation.
5859     if(properties)
5860     {
5861         memcpy(result, properties, alloc_size);
5862     }
5863 
5864     // return result.
5865     return result;
5866 }
5867 
5868 // Caskey, Damon V.
5869 // 2016-11-27
5870 //
5871 // Allocate an empty collision attack list.
collision_alloc_body_list()5872 s_collision_body **collision_alloc_body_list()
5873 {
5874     s_collision_body **result;
5875     size_t             alloc_size;
5876 
5877     // Get amount of memory we'll need.
5878     alloc_size = max_collisons * sizeof(*result);
5879 
5880     // Allocate memory and get pointer.
5881     result = malloc(alloc_size);
5882 
5883     // Make sure the list is blank.
5884     memset(result, 0, alloc_size);
5885 
5886     // return result.
5887     return result;
5888 }
5889 
5890 // Caskey, Damon V.
5891 // 2016-11-26
5892 //
5893 // Allocate collision coordinates, copy coords
5894 // data if present, and return pointer.
collision_alloc_coords(s_hitbox * coords)5895 s_hitbox *collision_alloc_coords(s_hitbox *coords)
5896 {
5897     s_hitbox    *result;
5898     size_t      alloc_size;
5899 
5900     // Get amount of memory we'll need.
5901     alloc_size = sizeof(*result);
5902 
5903     // Allocate memory and get pointer.
5904     result = malloc(alloc_size);
5905 
5906     // If previous data is provided,
5907     // copy into new allocation.
5908     if(coords)
5909     {
5910         memcpy(result, coords, alloc_size);
5911     }
5912 
5913     // Return result.
5914     return result;
5915 }
5916 
addframe(s_anim * a,int spriteindex,int framecount,int delay,unsigned idle,s_collision_body * bbox,s_collision_attack * attack,s_axis_i * move,float * platform,int frameshadow,int * shadow_coords,int soundtoplay,s_drawmethod * drawmethod,s_axis_i_2d * offset,s_damage_recursive * recursive,s_hitbox * attack_coords,s_hitbox * body_coords)5917 int addframe(s_anim             *a,
5918              int                spriteindex,
5919              int                framecount,
5920              int                delay,
5921              unsigned           idle,
5922              s_collision_body   *bbox,
5923              s_collision_attack *attack,
5924              s_axis_i           *move,
5925              float              *platform,
5926              int                frameshadow,
5927              int                *shadow_coords,
5928              int                soundtoplay,
5929              s_drawmethod       *drawmethod,
5930              s_axis_i_2d        *offset,
5931              s_damage_recursive *recursive,
5932              s_hitbox           *attack_coords,
5933              s_hitbox           *body_coords)
5934 {
5935     int     i;
5936     size_t  size_col_on_frame,
5937             size_col_on_frame_struct;
5938 
5939     s_collision_attack  *collision_attack;
5940     s_collision_body    *collision_body;
5941 
5942     ptrdiff_t currentframe;
5943     if(framecount > 0)
5944     {
5945         alloc_frames(a, framecount);
5946     }
5947     else
5948     {
5949         framecount = -framecount;    // for alloc method, use a negative value
5950     }
5951 
5952     currentframe = a->numframes;
5953     ++a->numframes;
5954 
5955     a->sprite[currentframe] = spriteindex;
5956     a->delay[currentframe] = delay * GAME_SPEED / 100;
5957 
5958     // Allocate body boxes.
5959     if((body_coords->width - body_coords->x)
5960         && (body_coords->height - body_coords->y))
5961     {
5962         // Set vulnerability.
5963         a->vulnerable[currentframe] = 1;
5964 
5965         // If there is no previous collision
5966         // list on this animation's frames,
5967         // we need to allocate space.
5968         if(!a->collision_body)
5969         {
5970             // Get memory size.
5971             size_col_on_frame = framecount * sizeof(*a->collision_body);
5972 
5973             // Allocate memory.
5974             a->collision_body = malloc(size_col_on_frame);
5975             memset(a->collision_body, 0, size_col_on_frame);
5976         }
5977 
5978         // Get memory size for the collision
5979         // list structure.
5980         size_col_on_frame_struct = sizeof(**a->collision_body);
5981 
5982         // Allocate memory for collision list
5983         // structure and store resulting pointer
5984         // on current frame.
5985         a->collision_body[currentframe] = malloc(size_col_on_frame_struct);
5986 
5987         // Allocate memory for a pointer to each instance
5988         // and set empty default.
5989         a->collision_body[currentframe]->instance = collision_alloc_body_list();
5990 
5991         // Loop instances, allocate memory, and assign
5992         // user values.
5993         for(i=0; i<max_collisons; i++)
5994         {
5995             // Allocate memory for this instance structure.
5996             // We're also using a local pointer for readability.
5997             collision_body = collision_alloc_body_instance(bbox);
5998             a->collision_body[currentframe]->instance[i] = collision_body;
5999 
6000             // Set index. Engine does not need this,
6001             // but will allow scripts to identify
6002             // which item instance has been acquired
6003             // by handle (pointer).
6004             collision_body->index = i;
6005 
6006             // Now let's allocate sub-properties.
6007 
6008             // Coordinates.
6009             if(!collision_body->coords)
6010             {
6011                 collision_body->coords = collision_alloc_coords(body_coords);
6012             }
6013         }
6014     }
6015 
6016     // Allocate attack boxes. See body box
6017     // for notes.
6018     if((attack_coords->width - attack_coords->x) &&
6019             (attack_coords->height - attack_coords->y))
6020     {
6021         if(!a->collision_attack)
6022         {
6023             size_col_on_frame = framecount * sizeof(*a->collision_attack);
6024 
6025             a->collision_attack = malloc(size_col_on_frame);
6026             memset(a->collision_attack, 0, size_col_on_frame);
6027         }
6028 
6029         size_col_on_frame_struct = sizeof(**a->collision_attack);
6030         a->collision_attack[currentframe] = malloc(size_col_on_frame_struct);
6031 
6032         a->collision_attack[currentframe]->instance = collision_alloc_attack_list();
6033 
6034         for(i=0; i<max_collisons; i++)
6035         {
6036             collision_attack = collision_alloc_attack_instance(attack);
6037             a->collision_attack[currentframe]->instance[i] = collision_attack;
6038 
6039             collision_attack->index = i;
6040 
6041             // Coordinates.
6042             if(!collision_attack->coords)
6043             {
6044                 collision_attack->coords = collision_alloc_coords(attack_coords);
6045             }
6046 
6047             // Recursive damage.
6048             if(!collision_attack->recursive && recursive->mode)
6049             {
6050                 collision_attack->recursive = malloc(sizeof(*recursive));
6051                 memcpy(collision_attack->recursive, recursive, sizeof(*recursive));
6052             }
6053         }
6054     }
6055 
6056     if(drawmethod->flag)
6057     {
6058         if(!a->drawmethods)
6059         {
6060             a->drawmethods = malloc(framecount * sizeof(*a->drawmethods));
6061             memset(a->drawmethods, 0, framecount * sizeof(*a->drawmethods));
6062         }
6063         setDrawMethod(a, currentframe, malloc(sizeof(**a->drawmethods)));
6064         //a->drawmethods[currenframe] = malloc(sizeof(s_drawmethod));
6065         memcpy(getDrawMethod(a, currentframe), drawmethod, sizeof(**a->drawmethods));
6066         //memcpy(a->drawmethods[currentframe], drawmethod, sizeof(s_drawmethod));
6067     }
6068     if(idle && !a->idle)
6069     {
6070         a->idle = malloc(framecount * sizeof(*a->idle));
6071         memset(a->idle, 0, framecount * sizeof(*a->idle));
6072     }
6073     if(a->idle)
6074     {
6075         a->idle[currentframe] = idle;
6076     }
6077 
6078     if(move)
6079     {
6080         if(!a->move)
6081         {
6082             a->move = malloc(framecount * sizeof(*a->move));
6083             memset(a->move, 0, framecount * sizeof(*a->move));
6084         }
6085         a->move[currentframe] = malloc(sizeof(**a->move));
6086         memcpy(a->move[currentframe], move, sizeof(**a->move));
6087     }
6088 
6089     if(frameshadow >= 0 && !a->shadow)
6090     {
6091         a->shadow = malloc(framecount * sizeof(*a->shadow));
6092         memset(a->shadow, -1, framecount * sizeof(*a->shadow)); //default to -1
6093     }
6094     if(a->shadow)
6095     {
6096         a->shadow[currentframe] = frameshadow;    // shadow index for each frame
6097     }
6098     if(shadow_coords[0] || shadow_coords[1])
6099     {
6100         if(!a->shadow_coords)
6101         {
6102             a->shadow_coords = malloc(framecount * sizeof(*a->shadow_coords));
6103             memset(a->shadow_coords, 0, framecount * sizeof(*a->shadow_coords));
6104         }
6105         memcpy(a->shadow_coords[currentframe], shadow_coords, sizeof(*a->shadow_coords));
6106     }
6107 
6108     // Offset
6109     if(offset->x || offset->y)
6110     {
6111         if(!a->offset)
6112         {
6113             a->offset = malloc(framecount * sizeof(*a->offset));
6114             memset(a->offset, 0, framecount * sizeof(*a->offset));
6115         }
6116         a->offset[currentframe] = malloc(sizeof(**a->offset));
6117         memcpy(a->offset[currentframe], offset, sizeof(**a->offset));
6118     }
6119 
6120     if(platform[7]) //height
6121     {
6122         if(!a->platform)
6123         {
6124             a->platform = malloc(framecount * sizeof(*a->platform));
6125             memset(a->platform, 0, framecount * sizeof(*a->platform));
6126         }
6127         memcpy(a->platform[currentframe], platform, sizeof(*a->platform));// Used so entity can be landed on
6128     }
6129     if(soundtoplay >= 0)
6130     {
6131         if(!a->soundtoplay)
6132         {
6133             a->soundtoplay = malloc(framecount * sizeof(*a->soundtoplay));
6134             memset(a->soundtoplay, -1, framecount * sizeof(*a->soundtoplay)); // default to -1
6135         }
6136         a->soundtoplay[currentframe] = soundtoplay;
6137     }
6138 
6139     return a->numframes;
6140 }
6141 
6142 
6143 // ok this func only seems to overwrite the name which was assigned from models.txt with the one
6144 // in the models own text file.
6145 // it does so in the cache.
_peek_model_name(int index)6146 void _peek_model_name(int index)
6147 {
6148     size_t size = 0;
6149     ptrdiff_t pos = 0, len;
6150     char *buf = NULL;
6151     char *command, *value;
6152     ArgList arglist;
6153     char argbuf[MAX_ARG_LEN + 1] = "";
6154     modelCommands cmd;
6155 
6156     if(buffer_pakfile(model_cache[index].path, &buf, &size) != 1)
6157     {
6158         return;
6159     }
6160 
6161     while(pos < size)
6162     {
6163         ParseArgs(&arglist, buf + pos, argbuf);
6164         command = GET_ARG(0);
6165 
6166         if(command && command[0])
6167         {
6168             cmd = getModelCommand(modelcmdlist, command);
6169             if(cmd == CMD_MODEL_NAME)
6170             {
6171                 value = GET_ARG(1);
6172                 free(model_cache[index].name);
6173                 model_cache[index].name = NULL;
6174                 len = strlen(value);
6175                 model_cache[index].name = malloc(len + 1);
6176                 strcpy(model_cache[index].name, value);
6177                 model_cache[index].name[len] = 0;
6178                 break;
6179             }
6180         }
6181         pos += getNewLineStart(buf + pos);
6182     }
6183 
6184     if(buf != NULL)
6185     {
6186         free(buf);
6187         buf = NULL;
6188     }
6189 }
6190 
prepare_cache_map(size_t size)6191 void prepare_cache_map(size_t size)
6192 {
6193     if(model_cache == NULL || size + 1 > cache_map_max_items )
6194     {
6195 #ifdef VERBOSE
6196         printf("%s %p\n", "prepare_cache_map was", model_cache);
6197 #endif
6198         do
6199         {
6200             cache_map_max_items += 128;
6201         }
6202         while (size + 1 > cache_map_max_items);
6203 
6204         model_cache = realloc(model_cache, sizeof(*model_cache) * cache_map_max_items);
6205         if(model_cache == NULL)
6206         {
6207             shutdown(1, "Out Of Memory!  Failed to create a new cache_map\n");
6208         }
6209     }
6210 }
6211 
cache_model(char * name,char * path,int flag)6212 void cache_model(char *name, char *path, int flag)
6213 {
6214     int len;
6215     printf("Cacheing '%s' from %s\n", name, path);
6216     prepare_cache_map(models_cached + 1);
6217     memset(&model_cache[models_cached], 0, sizeof(model_cache[models_cached]));
6218 
6219     len = strlen(name);
6220     model_cache[models_cached].name = malloc(len + 1);
6221     strcpy(model_cache[models_cached].name, name);
6222     model_cache[models_cached].name[len] = 0;
6223 
6224     len = strlen(path);
6225     model_cache[models_cached].path = malloc(len + 1);
6226     strcpy(model_cache[models_cached].path, path);
6227     model_cache[models_cached].path[len] = 0;
6228 
6229     model_cache[models_cached].loadflag = flag;
6230 
6231     _peek_model_name(models_cached);
6232     ++models_cached;
6233 }
6234 
6235 
free_modelcache()6236 void free_modelcache()
6237 {
6238     if(model_cache != NULL)
6239     {
6240         while(models_cached)
6241         {
6242             --models_cached;
6243             free(model_cache[models_cached].name);
6244             model_cache[models_cached].name = NULL;
6245             free(model_cache[models_cached].path);
6246             model_cache[models_cached].path = NULL;
6247         }
6248         free(model_cache);
6249         model_cache = NULL;
6250     }
6251 }
6252 
6253 
get_cached_model_index(char * name)6254 int get_cached_model_index(char *name)
6255 {
6256     int i;
6257     for(i = 0; i < models_cached; i++)
6258     {
6259         if(stricmp(name, model_cache[i].name) == 0)
6260         {
6261             return i;
6262         }
6263     }
6264     return -1;
6265 }
6266 
get_cached_model_path(char * name)6267 char *get_cached_model_path(char *name)
6268 {
6269     int i;
6270     for(i = 0; i < models_cached; i++)
6271     {
6272         if(stricmp(name, model_cache[i].name) == 0)
6273         {
6274             return model_cache[i].path;
6275         }
6276     }
6277     return NULL;
6278 }
6279 
6280 static void _readbarstatus(char *, s_barstatus *);
6281 
6282 //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)6283 static int translate_ani_id(const char *value, s_model *newchar, s_anim *newanim, s_collision_attack *attack)
6284 {
6285     int ani_id = -1, tempInt;
6286     //those are dummy values to simplify code
6287     static s_model mdl;
6288     static s_anim ani;
6289     static s_collision_attack atk;
6290     if(!newchar)
6291     {
6292         newchar = &mdl;
6293     }
6294     if(!newanim)
6295     {
6296         newanim = &ani;
6297     }
6298     if(!attack)
6299     {
6300         attack = &atk;
6301     }
6302 
6303     if(starts_with_num(value, "idle"))
6304     {
6305         get_tail_number(tempInt, value, "idle");
6306         ani_id = animidles[tempInt - 1];
6307     }
6308     else if(stricmp(value, "waiting") == 0)
6309     {
6310         ani_id = ANI_SELECT;
6311     }
6312     else if(starts_with_num(value, "walk"))
6313     {
6314         get_tail_number(tempInt, value, "walk");
6315         ani_id = animwalks[tempInt - 1];
6316         newanim->sync = ANI_WALK;
6317 
6318     }
6319     else if(stricmp(value, "sleep") == 0)
6320     {
6321         ani_id = ANI_SLEEP;
6322     }
6323     else if(stricmp(value, "run") == 0)
6324     {
6325         ani_id = ANI_RUN;
6326     }
6327     else if(stricmp(value, "backrun") == 0)
6328     {
6329         ani_id = ANI_BACKRUN;
6330     }
6331     else if(stricmp(value, "getboomerang") == 0)
6332     {
6333         ani_id = ANI_GETBOOMERANG;
6334     }
6335     else if(stricmp(value, "getboomeranginair") == 0)
6336     {
6337         ani_id = ANI_GETBOOMERANGINAIR;
6338     }
6339     else if(starts_with_num(value, "up"))
6340     {
6341         get_tail_number(tempInt, value, "up");
6342         ani_id = animups[tempInt - 1];
6343         newanim->sync = ANI_WALK;
6344     }
6345     else if(starts_with_num(value, "down"))
6346     {
6347         get_tail_number(tempInt, value, "down");
6348         ani_id = animdowns[tempInt - 1];
6349         newanim->sync = ANI_WALK;
6350     }
6351     else if(starts_with_num(value, "backwalk"))
6352     {
6353         get_tail_number(tempInt, value, "backwalk");
6354         ani_id = animbackwalks[tempInt - 1];
6355         newanim->sync = ANI_WALK;
6356     }
6357     else if(stricmp(value, "jump") == 0)
6358     {
6359         ani_id = ANI_JUMP;
6360         newanim->range.min.x = 50;
6361         newanim->range.max.x = 60;
6362     }
6363     else if(stricmp(value, "duck") == 0)
6364     {
6365         ani_id = ANI_DUCK;
6366     }
6367     else if(stricmp(value, "land") == 0)
6368     {
6369         ani_id = ANI_LAND;
6370     }
6371     else if(starts_with_num(value, "pain"))
6372     {
6373         get_tail_number(tempInt, value, "pain");
6374         if(tempInt == 1)
6375         {
6376             ani_id = ANI_PAIN;
6377         }
6378         else if(tempInt == 2)
6379         {
6380             ani_id = ANI_PAIN2;
6381         }
6382         else if(tempInt == 3)
6383         {
6384             ani_id = ANI_PAIN3;
6385         }
6386         else if(tempInt == 4)
6387         {
6388             ani_id = ANI_PAIN4;
6389         }
6390         else if(tempInt == 5)
6391         {
6392             ani_id = ANI_PAIN5;
6393         }
6394         else if(tempInt == 6)
6395         {
6396             ani_id = ANI_PAIN6;
6397         }
6398         else if(tempInt == 7)
6399         {
6400             ani_id = ANI_PAIN7;
6401         }
6402         else if(tempInt == 8)
6403         {
6404             ani_id = ANI_PAIN8;
6405         }
6406         else if(tempInt == 9)
6407         {
6408             ani_id = ANI_PAIN9;
6409         }
6410         else if(tempInt == 10)
6411         {
6412             ani_id = ANI_PAIN10;
6413         }
6414         else
6415         {
6416             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6417             {
6418                 tempInt = MAX_ATKS - STA_ATKS + 1;
6419             }
6420             ani_id = animpains[tempInt + STA_ATKS - 1];
6421         }
6422     }
6423     else if(starts_with_num(value, "backpain"))
6424     {
6425         get_tail_number(tempInt, value, "backpain");
6426         if(tempInt == 1)
6427         {
6428             ani_id = ANI_BACKPAIN;
6429         }
6430         else if(tempInt == 2)
6431         {
6432             ani_id = ANI_BACKPAIN2;
6433         }
6434         else if(tempInt == 3)
6435         {
6436             ani_id = ANI_BACKPAIN3;
6437         }
6438         else if(tempInt == 4)
6439         {
6440             ani_id = ANI_BACKPAIN4;
6441         }
6442         else if(tempInt == 5)
6443         {
6444             ani_id = ANI_BACKPAIN5;
6445         }
6446         else if(tempInt == 6)
6447         {
6448             ani_id = ANI_BACKPAIN6;
6449         }
6450         else if(tempInt == 7)
6451         {
6452             ani_id = ANI_BACKPAIN7;
6453         }
6454         else if(tempInt == 8)
6455         {
6456             ani_id = ANI_BACKPAIN8;
6457         }
6458         else if(tempInt == 9)
6459         {
6460             ani_id = ANI_BACKPAIN9;
6461         }
6462         else if(tempInt == 10)
6463         {
6464             ani_id = ANI_BACKPAIN10;
6465         }
6466         else
6467         {
6468             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6469             {
6470                 tempInt = MAX_ATKS - STA_ATKS + 1;
6471             }
6472             ani_id = animbackpains[tempInt + STA_ATKS - 1];
6473         }
6474     }
6475     else if(stricmp(value, "spain") == 0)   // If shock attacks don't knock opponent down, play this
6476     {
6477         ani_id = ANI_SHOCKPAIN;
6478     }
6479     else if(stricmp(value, "bpain") == 0)   // If burn attacks don't knock opponent down, play this
6480     {
6481         ani_id = ANI_BURNPAIN;
6482     }
6483     else if(starts_with_num(value, "fall"))
6484     {
6485         get_tail_number(tempInt, value, "fall");
6486         if(tempInt == 1)
6487         {
6488             ani_id = ANI_FALL;
6489         }
6490         else if(tempInt == 2)
6491         {
6492             ani_id = ANI_FALL2;
6493         }
6494         else if(tempInt == 3)
6495         {
6496             ani_id = ANI_FALL3;
6497         }
6498         else if(tempInt == 4)
6499         {
6500             ani_id = ANI_FALL4;
6501         }
6502         else if(tempInt == 5)
6503         {
6504             ani_id = ANI_FALL5;
6505         }
6506         else if(tempInt == 6)
6507         {
6508             ani_id = ANI_FALL6;
6509         }
6510         else if(tempInt == 7)
6511         {
6512             ani_id = ANI_FALL7;
6513         }
6514         else if(tempInt == 8)
6515         {
6516             ani_id = ANI_FALL8;
6517         }
6518         else if(tempInt == 9)
6519         {
6520             ani_id = ANI_FALL9;
6521         }
6522         else if(tempInt == 10)
6523         {
6524             ani_id = ANI_FALL10;
6525         }
6526         else
6527         {
6528             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6529             {
6530                 tempInt = MAX_ATKS - STA_ATKS + 1;
6531             }
6532             ani_id = animfalls[tempInt + STA_ATKS - 1];
6533         }
6534         newanim->bounce = 4;
6535     }
6536     else if(starts_with_num(value, "backfall"))
6537     {
6538         get_tail_number(tempInt, value, "backfall");
6539         if(tempInt == 1)
6540         {
6541             ani_id = ANI_BACKFALL;
6542         }
6543         else if(tempInt == 2)
6544         {
6545             ani_id = ANI_BACKFALL2;
6546         }
6547         else if(tempInt == 3)
6548         {
6549             ani_id = ANI_BACKFALL3;
6550         }
6551         else if(tempInt == 4)
6552         {
6553             ani_id = ANI_BACKFALL4;
6554         }
6555         else if(tempInt == 5)
6556         {
6557             ani_id = ANI_BACKFALL5;
6558         }
6559         else if(tempInt == 6)
6560         {
6561             ani_id = ANI_BACKFALL6;
6562         }
6563         else if(tempInt == 7)
6564         {
6565             ani_id = ANI_BACKFALL7;
6566         }
6567         else if(tempInt == 8)
6568         {
6569             ani_id = ANI_BACKFALL8;
6570         }
6571         else if(tempInt == 9)
6572         {
6573             ani_id = ANI_BACKFALL9;
6574         }
6575         else if(tempInt == 10)
6576         {
6577             ani_id = ANI_BACKFALL10;
6578         }
6579         else
6580         {
6581             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6582             {
6583                 tempInt = MAX_ATKS - STA_ATKS + 1;
6584             }
6585             ani_id = animbackfalls[tempInt + STA_ATKS - 1];
6586         }
6587         newanim->bounce = 4;
6588     }
6589     else if(stricmp(value, "shock") == 0)   // If shock attacks do knock opponent down, play this
6590     {
6591         ani_id = ANI_SHOCK;
6592         newanim->bounce = 4;
6593     }
6594     else if(stricmp(value, "burn") == 0)   // If burn attacks do knock opponent down, play this
6595     {
6596         ani_id = ANI_BURN;
6597         newanim->bounce = 4;
6598     }
6599     else if(starts_with_num(value, "death"))
6600     {
6601         get_tail_number(tempInt, value, "death");
6602         if(tempInt == 1)
6603         {
6604             ani_id = ANI_DIE;
6605         }
6606         else if(tempInt == 2)
6607         {
6608             ani_id = ANI_DIE2;
6609         }
6610         else if(tempInt == 3)
6611         {
6612             ani_id = ANI_DIE3;
6613         }
6614         else if(tempInt == 4)
6615         {
6616             ani_id = ANI_DIE4;
6617         }
6618         else if(tempInt == 5)
6619         {
6620             ani_id = ANI_DIE5;
6621         }
6622         else if(tempInt == 6)
6623         {
6624             ani_id = ANI_DIE6;
6625         }
6626         else if(tempInt == 7)
6627         {
6628             ani_id = ANI_DIE7;
6629         }
6630         else if(tempInt == 8)
6631         {
6632             ani_id = ANI_DIE8;
6633         }
6634         else if(tempInt == 9)
6635         {
6636             ani_id = ANI_DIE9;
6637         }
6638         else if(tempInt == 10)
6639         {
6640             ani_id = ANI_DIE10;
6641         }
6642         else
6643         {
6644             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6645             {
6646                 tempInt = MAX_ATKS - STA_ATKS + 1;
6647             }
6648             ani_id = animdies[tempInt + STA_ATKS - 1];
6649         }
6650     }
6651     else if(starts_with_num(value, "backdeath"))
6652     {
6653         get_tail_number(tempInt, value, "backdeath");
6654         if(tempInt == 1)
6655         {
6656             ani_id = ANI_BACKDIE;
6657         }
6658         else if(tempInt == 2)
6659         {
6660             ani_id = ANI_BACKDIE2;
6661         }
6662         else if(tempInt == 3)
6663         {
6664             ani_id = ANI_BACKDIE3;
6665         }
6666         else if(tempInt == 4)
6667         {
6668             ani_id = ANI_BACKDIE4;
6669         }
6670         else if(tempInt == 5)
6671         {
6672             ani_id = ANI_BACKDIE5;
6673         }
6674         else if(tempInt == 6)
6675         {
6676             ani_id = ANI_BACKDIE6;
6677         }
6678         else if(tempInt == 7)
6679         {
6680             ani_id = ANI_BACKDIE7;
6681         }
6682         else if(tempInt == 8)
6683         {
6684             ani_id = ANI_BACKDIE8;
6685         }
6686         else if(tempInt == 9)
6687         {
6688             ani_id = ANI_BACKDIE9;
6689         }
6690         else if(tempInt == 10)
6691         {
6692             ani_id = ANI_BACKDIE10;
6693         }
6694         else
6695         {
6696             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6697             {
6698                 tempInt = MAX_ATKS - STA_ATKS + 1;
6699             }
6700             ani_id = animbackdies[tempInt + STA_ATKS - 1];
6701         }
6702     }
6703     else if(stricmp(value, "sdie") == 0)
6704     {
6705         ani_id = ANI_SHOCKDIE;
6706     }
6707     else if(stricmp(value, "bdie") == 0)
6708     {
6709         ani_id = ANI_BURNDIE;
6710     }
6711     else if(stricmp(value, "chipdeath") == 0)
6712     {
6713         ani_id = ANI_CHIPDEATH;
6714     }
6715     else if(stricmp(value, "guardbreak") == 0)
6716     {
6717         ani_id = ANI_GUARDBREAK;
6718     }
6719     else if(stricmp(value, "riseb") == 0)
6720     {
6721         ani_id = ANI_RISEB;
6722     }
6723     else if(stricmp(value, "rises") == 0)
6724     {
6725         ani_id = ANI_RISES;
6726     }
6727     else if(starts_with_num(value, "rise"))
6728     {
6729         get_tail_number(tempInt, value, "rise");
6730         if(tempInt == 1)
6731         {
6732             ani_id = ANI_RISE;
6733         }
6734         else if(tempInt == 2)
6735         {
6736             ani_id = ANI_RISE2;
6737         }
6738         else if(tempInt == 3)
6739         {
6740             ani_id = ANI_RISE3;
6741         }
6742         else if(tempInt == 4)
6743         {
6744             ani_id = ANI_RISE4;
6745         }
6746         else if(tempInt == 5)
6747         {
6748             ani_id = ANI_RISE5;
6749         }
6750         else if(tempInt == 6)
6751         {
6752             ani_id = ANI_RISE6;
6753         }
6754         else if(tempInt == 7)
6755         {
6756             ani_id = ANI_RISE7;
6757         }
6758         else if(tempInt == 8)
6759         {
6760             ani_id = ANI_RISE8;
6761         }
6762         else if(tempInt == 9)
6763         {
6764             ani_id = ANI_RISE9;
6765         }
6766         else if(tempInt == 10)
6767         {
6768             ani_id = ANI_RISE10;
6769         }
6770         else
6771         {
6772             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6773             {
6774                 tempInt = MAX_ATKS - STA_ATKS + 1;
6775             }
6776             ani_id = animrises[tempInt + STA_ATKS - 1];
6777         }
6778     }
6779     else if(stricmp(value, "riseattackb") == 0)
6780     {
6781         ani_id = ANI_RISEATTACKB;
6782     }
6783     else if(stricmp(value, "riseattacks") == 0)
6784     {
6785         ani_id = ANI_RISEATTACKS;
6786     }
6787     else if(starts_with_num(value, "riseattack"))
6788     {
6789         get_tail_number(tempInt, value, "riseattack");
6790         if(tempInt == 1)
6791         {
6792             ani_id = ANI_RISEATTACK;
6793         }
6794         else if(tempInt == 2)
6795         {
6796             ani_id = ANI_RISEATTACK2;
6797         }
6798         else if(tempInt == 3)
6799         {
6800             ani_id = ANI_RISEATTACK3;
6801         }
6802         else if(tempInt == 4)
6803         {
6804             ani_id = ANI_RISEATTACK4;
6805         }
6806         else if(tempInt == 6)
6807         {
6808             ani_id = ANI_RISEATTACK5;
6809         }
6810         else if(tempInt == 6)
6811         {
6812             ani_id = ANI_RISEATTACK6;
6813         }
6814         else if(tempInt == 7)
6815         {
6816             ani_id = ANI_RISEATTACK7;
6817         }
6818         else if(tempInt == 8)
6819         {
6820             ani_id = ANI_RISEATTACK8;
6821         }
6822         else if(tempInt == 9)
6823         {
6824             ani_id = ANI_RISEATTACK9;
6825         }
6826         else if(tempInt == 10)
6827         {
6828             ani_id = ANI_RISEATTACK10;
6829         }
6830         else
6831         {
6832             if(tempInt < MAX_ATKS - STA_ATKS + 1)
6833             {
6834                 tempInt = MAX_ATKS - STA_ATKS + 1;
6835             }
6836             ani_id = animriseattacks[tempInt + STA_ATKS - 1];
6837         }
6838     }
6839     else if(stricmp(value, "select") == 0)
6840     {
6841         ani_id = ANI_PICK;
6842     }
6843     else if(starts_with_num(value, "attack"))
6844     {
6845         get_tail_number(tempInt, value, "attack");
6846         ani_id = animattacks[tempInt - 1];
6847     }
6848     else if(stricmp(value, "throwattack") == 0)
6849     {
6850         ani_id = ANI_THROWATTACK;
6851     }
6852     else if(stricmp(value, "upper") == 0)
6853     {
6854         ani_id = ANI_UPPER;
6855         attack->counterattack = 100; //default to 100
6856         newanim->range.min.x = -10;
6857         newanim->range.max.x = 120;
6858     }
6859     else if(stricmp(value, "cant") == 0)
6860     {
6861         ani_id = ANI_CANT;
6862     }
6863     else if(stricmp(value, "jumpcant") == 0)
6864     {
6865         ani_id = ANI_JUMPCANT;
6866     }
6867     else if(stricmp(value, "charge") == 0)
6868     {
6869         ani_id = ANI_CHARGE;
6870     }
6871     else if(stricmp(value, "faint") == 0)
6872     {
6873         ani_id = ANI_FAINT;
6874     }
6875     else if(stricmp(value, "dodge") == 0)
6876     {
6877         ani_id = ANI_DODGE;
6878     }
6879     else if(stricmp(value, "special") == 0 || stricmp(value, "special1") == 0)
6880     {
6881         ani_id = ANI_SPECIAL;
6882         if(!newanim->energycost)
6883         {
6884             newanim->energycost         = malloc(sizeof(*newanim->energycost));
6885             memset(newanim->energycost, 0, sizeof(*newanim->energycost));
6886 
6887             newanim->energycost->cost   = ENERGYCOST_DEFAULT_COST;
6888             newanim->energycost->mponly = COST_TYPE_MP_THEN_HP;
6889         }
6890     }
6891     else if(stricmp(value, "special2") == 0)
6892     {
6893         ani_id = ANI_SPECIAL2;
6894         if(!newanim->energycost)
6895         {
6896             newanim->energycost         = malloc(sizeof(*newanim->energycost));
6897             memset(newanim->energycost, 0, sizeof(*newanim->energycost));
6898 
6899             newanim->energycost->cost   = ENERGYCOST_NOCOST;
6900             newanim->energycost->mponly = COST_TYPE_MP_THEN_HP;
6901         }
6902     }
6903     else if(stricmp(value, "special3") == 0 || stricmp(value, "jumpspecial") == 0)
6904     {
6905         ani_id = ANI_JUMPSPECIAL;
6906         if(!newanim->energycost)
6907         {
6908             newanim->energycost         = malloc(sizeof(*newanim->energycost));
6909             memset(newanim->energycost, 0, sizeof(*newanim->energycost));
6910 
6911             newanim->energycost->cost   = ENERGYCOST_NOCOST;
6912             newanim->energycost->mponly = COST_TYPE_MP_THEN_HP;
6913         }
6914     }
6915     else if(starts_with_num(value, "freespecial"))
6916     {
6917         get_tail_number(tempInt, value, "freespecial");
6918         ani_id = animspecials[tempInt - 1];
6919     }
6920     else if(stricmp(value, "jumpattack") == 0)
6921     {
6922         ani_id = ANI_JUMPATTACK;
6923         if(newchar->jumpheight == 4)
6924         {
6925             newanim->range.min.x = 150;
6926             newanim->range.max.x = 200;
6927         }
6928     }
6929     else if(stricmp(value, "jumpattack2") == 0)
6930     {
6931         ani_id = ANI_JUMPATTACK2;
6932     }
6933     else if(stricmp(value, "jumpattack3") == 0)
6934     {
6935         ani_id = ANI_JUMPATTACK3;
6936     }
6937     else if(stricmp(value, "jumpforward") == 0)
6938     {
6939         ani_id = ANI_JUMPFORWARD;
6940     }
6941     else if(stricmp(value, "runjumpattack") == 0)
6942     {
6943         ani_id = ANI_RUNJUMPATTACK;
6944     }
6945     else if(stricmp(value, "runattack") == 0)
6946     {
6947         ani_id = ANI_RUNATTACK;    // New attack for when a player is running
6948     }
6949     else if(stricmp(value, "attackup") == 0)
6950     {
6951         ani_id = ANI_ATTACKUP;    // New attack for when a player presses u u
6952     }
6953     else if(stricmp(value, "attackdown") == 0)
6954     {
6955         ani_id = ANI_ATTACKDOWN;    // New attack for when a player presses d d
6956     }
6957     else if(stricmp(value, "attackforward") == 0)
6958     {
6959         ani_id = ANI_ATTACKFORWARD;    // New attack for when a player presses f f
6960     }
6961     else if(stricmp(value, "attackbackward") == 0)
6962     {
6963         ani_id = ANI_ATTACKBACKWARD;    // New attack for when a player presses b a
6964     }
6965     else if(stricmp(value, "attackboth") == 0)   // Attack that is executed by holding down j and pressing a
6966     {
6967         ani_id = ANI_ATTACKBOTH;
6968     }
6969     else if(stricmp(value, "get") == 0)
6970     {
6971         ani_id = ANI_GET;
6972     }
6973     else if(stricmp(value, "grab") == 0)
6974     {
6975         ani_id = ANI_GRAB;
6976     }
6977     else if(stricmp(value, "grabwalk") == 0)
6978     {
6979         ani_id = ANI_GRABWALK;
6980         newanim->sync = ANI_GRABWALK;
6981     }
6982     else if(stricmp(value, "grabwalkup") == 0)
6983     {
6984         ani_id = ANI_GRABWALKUP;
6985         newanim->sync = ANI_GRABWALK;
6986     }
6987     else if(stricmp(value, "grabwalkdown") == 0)
6988     {
6989         ani_id = ANI_GRABWALKDOWN;
6990         newanim->sync = ANI_GRABWALK;
6991     }
6992     else if(stricmp(value, "grabbackwalk") == 0)
6993     {
6994         ani_id = ANI_GRABBACKWALK;
6995         newanim->sync = ANI_GRABWALK;
6996     }
6997     else if(stricmp(value, "grabturn") == 0)
6998     {
6999         ani_id = ANI_GRABTURN;
7000     }
7001     else if(stricmp(value, "grabbed") == 0)   // New grabbed animation for when grabbed
7002     {
7003         ani_id = ANI_GRABBED;
7004     }
7005     else if(stricmp(value, "grabbedwalk") == 0)   // New animation for when grabbed and forced to walk
7006     {
7007         ani_id = ANI_GRABBEDWALK;
7008         newanim->sync = ANI_GRABBEDWALK;
7009     }
7010     else if(stricmp(value, "grabbedwalkup") == 0)
7011     {
7012         ani_id = ANI_GRABWALKUP;
7013         newanim->sync = ANI_GRABBEDWALK;
7014     }
7015     else if(stricmp(value, "grabbedwalkdown") == 0)
7016     {
7017         ani_id = ANI_GRABWALKDOWN;
7018         newanim->sync = ANI_GRABBEDWALK;
7019     }
7020     else if(stricmp(value, "grabbedbackwalk") == 0)
7021     {
7022         ani_id = ANI_GRABBEDBACKWALK;
7023         newanim->sync = ANI_GRABBEDWALK;
7024     }
7025     else if(stricmp(value, "grabbedturn") == 0)
7026     {
7027         ani_id = ANI_GRABBEDTURN;
7028     }
7029     else if(stricmp(value, "grabattack") == 0)
7030     {
7031         ani_id = ANI_GRABATTACK;
7032         newanim->attackone = 1; // default to 1, attack one one opponent
7033     }
7034     else if(stricmp(value, "grabattack2") == 0)
7035     {
7036         ani_id = ANI_GRABATTACK2;
7037         newanim->attackone = 1;
7038     }
7039     else if(stricmp(value, "grabforward") == 0)   // New grab attack for when pressing forward attack
7040     {
7041         ani_id = ANI_GRABFORWARD;
7042         newanim->attackone = 1;
7043     }
7044     else if(stricmp(value, "grabforward2") == 0)   // New grab attack for when pressing forward attack
7045     {
7046         ani_id = ANI_GRABFORWARD2;
7047         newanim->attackone = 1;
7048     }
7049     else if(stricmp(value, "grabbackward") == 0)   // New grab attack for when pressing backward attack
7050     {
7051         ani_id = ANI_GRABBACKWARD;
7052         newanim->attackone = 1;
7053     }
7054     else if(stricmp(value, "grabbackward2") == 0)   // New grab attack for when pressing backward attack
7055     {
7056         ani_id = ANI_GRABBACKWARD2;
7057         newanim->attackone = 1;
7058     }
7059     else if(stricmp(value, "grabup") == 0)   // New grab attack for when pressing up attack
7060     {
7061         ani_id = ANI_GRABUP;
7062         newanim->attackone = 1;
7063     }
7064     else if(stricmp(value, "grabup2") == 0)   // New grab attack for when pressing up attack
7065     {
7066         ani_id = ANI_GRABUP2;
7067         newanim->attackone = 1;
7068     }
7069     else if(stricmp(value, "grabdown") == 0)   // New grab attack for when pressing down attack
7070     {
7071         ani_id = ANI_GRABDOWN;
7072         newanim->attackone = 1;
7073     }
7074     else if(stricmp(value, "grabdown2") == 0)   // New grab attack for when pressing down attack
7075     {
7076         ani_id = ANI_GRABDOWN2;
7077         newanim->attackone = 1;
7078     }
7079     else if(stricmp(value, "spawn") == 0)     //  spawn/respawn works separately now
7080     {
7081         ani_id = ANI_SPAWN;
7082     }
7083     else if(stricmp(value, "respawn") == 0)     //  spawn/respawn works separately now
7084     {
7085         ani_id = ANI_RESPAWN;
7086     }
7087     else if(stricmp(value, "throw") == 0)
7088     {
7089         ani_id = ANI_THROW;
7090     }
7091     else if(stricmp(value, "block") == 0)   // Now enemies can block attacks on occasion
7092     {
7093         ani_id = ANI_BLOCK;
7094         newanim->range.min.x = 1;
7095         newanim->range.max.x = 100;
7096     }
7097     else if(starts_with_num(value, "follow"))
7098     {
7099         get_tail_number(tempInt, value, "follow");
7100         ani_id = animfollows[tempInt - 1];
7101     }
7102     else if(stricmp(value, "chargeattack") == 0)
7103     {
7104         ani_id = ANI_CHARGEATTACK;
7105     }
7106     else if(stricmp(value, "turn") == 0)
7107     {
7108         ani_id = ANI_TURN;
7109     }
7110     else if(stricmp(value, "forwardjump") == 0)
7111     {
7112         ani_id = ANI_FORWARDJUMP;
7113     }
7114     else if(stricmp(value, "runjump") == 0)
7115     {
7116         ani_id = ANI_RUNJUMP;
7117     }
7118     else if(stricmp(value, "jumpland") == 0)
7119     {
7120         ani_id = ANI_JUMPLAND;
7121     }
7122     else if(stricmp(value, "jumpdelay") == 0)
7123     {
7124         ani_id = ANI_JUMPDELAY;
7125     }
7126     else if(stricmp(value, "hitobstacle") == 0)
7127     {
7128         ani_id = ANI_HITOBSTACLE;
7129     }
7130     else if(stricmp(value, "hitplatform") == 0)
7131     {
7132         ani_id = ANI_HITPLATFORM;
7133     }
7134     else if(stricmp(value, "hitwall") == 0)
7135     {
7136         ani_id = ANI_HITWALL;
7137     }
7138     else if(stricmp(value, "slide") == 0)
7139     {
7140         ani_id = ANI_SLIDE;
7141     }
7142     else if(stricmp(value, "runslide") == 0)
7143     {
7144         ani_id = ANI_RUNSLIDE;
7145     }
7146     else if(stricmp(value, "blockpainb") == 0)
7147     {
7148         ani_id = ANI_BLOCKPAINB;
7149     }
7150     else if(stricmp(value, "blockpains") == 0)
7151     {
7152         ani_id = ANI_BLOCKPAINS;
7153     }
7154     else if(starts_with_num(value, "blockpain"))
7155     {
7156         get_tail_number(tempInt, value, "blockpain");
7157         if(tempInt == 1)
7158         {
7159             ani_id = ANI_BLOCKPAIN;
7160         }
7161         else if(tempInt == 2)
7162         {
7163             ani_id = ANI_BLOCKPAIN2;
7164         }
7165         else if(tempInt == 3)
7166         {
7167             ani_id = ANI_BLOCKPAIN3;
7168         }
7169         else if(tempInt == 4)
7170         {
7171             ani_id = ANI_BLOCKPAIN4;
7172         }
7173         else if(tempInt == 5)
7174         {
7175             ani_id = ANI_BLOCKPAIN5;
7176         }
7177         else if(tempInt == 6)
7178         {
7179             ani_id = ANI_BLOCKPAIN6;
7180         }
7181         else if(tempInt == 7)
7182         {
7183             ani_id = ANI_BLOCKPAIN7;
7184         }
7185         else if(tempInt == 8)
7186         {
7187             ani_id = ANI_BLOCKPAIN8;
7188         }
7189         else if(tempInt == 9)
7190         {
7191             ani_id = ANI_BLOCKPAIN9;
7192         }
7193         else if(tempInt == 10)
7194         {
7195             ani_id = ANI_BLOCKPAIN10;
7196         }
7197         else
7198         {
7199             if(tempInt < MAX_ATKS - STA_ATKS + 1)
7200             {
7201                 tempInt = MAX_ATKS - STA_ATKS + 1;
7202             }
7203             ani_id = animblkpains[tempInt + STA_ATKS - 1];
7204         }
7205     }
7206     else if(stricmp(value, "duckattack") == 0)
7207     {
7208         ani_id = ANI_DUCKATTACK;
7209     }
7210     else if(stricmp(value, "walkoff") == 0)
7211     {
7212         ani_id = ANI_WALKOFF;
7213     }
7214 
7215     return ani_id;
7216 
7217 }
7218 
lcmHandleCommandName(ArgList * arglist,s_model * newchar,int cacheindex)7219 void lcmHandleCommandName(ArgList *arglist, s_model *newchar, int cacheindex)
7220 {
7221     char *value = GET_ARGP(1);
7222     s_model *tempmodel;
7223     if((tempmodel = findmodel(value)) && tempmodel != newchar)
7224     {
7225         shutdown(1, "Duplicate model name '%s'", value);
7226     }
7227     /*if((tempmodel=find_model(value))) {
7228     	return tempmodel;
7229     }*/
7230     model_cache[cacheindex].model = newchar;
7231     newchar->name = model_cache[cacheindex].name;
7232     if(stricmp(newchar->name, "steam") == 0)
7233     {
7234         newchar->alpha = 1;
7235     }
7236 }
7237 
lcmHandleCommandType(ArgList * arglist,s_model * newchar,char * filename)7238 void lcmHandleCommandType(ArgList *arglist, s_model *newchar, char *filename)
7239 {
7240     char *value = GET_ARGP(1);
7241     int i;
7242     if(stricmp(value, "none") == 0)
7243     {
7244         newchar->type = TYPE_NONE;
7245     }
7246     else if(stricmp(value, "player") == 0)
7247     {
7248         newchar->type = TYPE_PLAYER;
7249         newchar->nopassiveblock = 1;
7250         for(i = 0; i < MAX_ATCHAIN; i++)
7251         {
7252             if(i < 2 || i > 3)
7253             {
7254                 newchar->atchain[i] = 1;
7255             }
7256             else
7257             {
7258                 newchar->atchain[i] = i;
7259             }
7260         }
7261         newchar->chainlength            = 4;
7262         newchar->bounce                 = 1;
7263         newchar->subject_to_basemap     = 1;
7264         newchar->subject_to_wall        = 1;
7265         newchar->subject_to_platform    = 1;
7266         newchar->subject_to_obstacle    = 1;
7267         newchar->subject_to_hole        = 1;
7268         newchar->subject_to_gravity     = 1;
7269         newchar->subject_to_screen      = 1;
7270         newchar->subject_to_minz        = 1;
7271         newchar->subject_to_maxz        = 1;
7272         newchar->no_adjust_base         = 0;
7273     }
7274     else if(stricmp(value, "enemy") == 0)
7275     {
7276         newchar->type                   = TYPE_ENEMY;
7277         newchar->bounce                 = 1;
7278         newchar->subject_to_basemap     = 1;
7279         newchar->subject_to_wall        = 1;
7280         newchar->subject_to_platform    = 1;
7281         newchar->subject_to_hole        = 1;
7282         newchar->subject_to_obstacle    = 1;
7283         newchar->subject_to_gravity     = 1;
7284         newchar->subject_to_minz        = 1;
7285         newchar->subject_to_maxz        = 1;
7286         newchar->no_adjust_base         = 0;
7287     }
7288     else if(stricmp(value, "item") == 0)
7289     {
7290         newchar->type                   = TYPE_ITEM;
7291         newchar->subject_to_basemap     = 1;
7292         newchar->subject_to_wall        = 1;
7293         newchar->subject_to_platform    = 1;
7294         newchar->subject_to_hole        = 1;
7295         newchar->subject_to_obstacle    = 1;
7296         newchar->subject_to_gravity     = 1;
7297         newchar->subject_to_minz        = 1;
7298         newchar->subject_to_maxz        = 1;
7299         newchar->no_adjust_base         = 0;
7300     }
7301     else if(stricmp(value, "obstacle") == 0)
7302     {
7303         newchar->type                   = TYPE_OBSTACLE;
7304         if(newchar->aimove == -1)
7305         {
7306             newchar->aimove = 0;
7307         }
7308         newchar->aimove |= AIMOVE1_NOMOVE;
7309         if(newchar->aimove == -1)
7310         {
7311             newchar->aiattack = 0;
7312         }
7313         newchar->aimove |= AIATTACK1_NOATTACK;
7314         newchar->subject_to_basemap     = 1;
7315         newchar->subject_to_wall        = 1;
7316         newchar->subject_to_platform    = 1;
7317         newchar->subject_to_hole        = 1;
7318         newchar->subject_to_gravity     = 1;
7319         newchar->subject_to_minz        = 1;
7320         newchar->subject_to_maxz        = 1;
7321         newchar->no_adjust_base         = 0;
7322     }
7323     else if(stricmp(value, "steamer") == 0)
7324     {
7325         newchar->offscreenkill = 80;
7326         newchar->type = TYPE_STEAMER;
7327     }
7328     // my new types   7-1-2005
7329     else if(stricmp(value, "pshot") == 0)
7330     {
7331         newchar->type = TYPE_SHOT;
7332         if(newchar->aimove == -1)
7333         {
7334             newchar->aimove = 0;
7335         }
7336         newchar->aimove |= AIMOVE1_ARROW;
7337         if(!newchar->offscreenkill)
7338         {
7339             newchar->offscreenkill = 200;
7340         }
7341         newchar->subject_to_hole                = 0;
7342         newchar->subject_to_gravity             = 1;
7343         newchar->subject_to_basemap             = 0;
7344         newchar->subject_to_wall                = 0;
7345         newchar->subject_to_platform            = 0;
7346         newchar->subject_to_screen              = 0;
7347         newchar->subject_to_minz                = 1;
7348         newchar->subject_to_maxz                = 1;
7349         newchar->subject_to_platform            = 0;
7350         newchar->no_adjust_base                 = 1;
7351     }
7352     else if(stricmp(value, "trap") == 0)
7353     {
7354         newchar->type                   = TYPE_TRAP;
7355         newchar->subject_to_basemap     = 1;
7356         newchar->subject_to_wall        = 1;
7357         newchar->subject_to_platform    = 1;
7358         newchar->subject_to_hole        = 1;
7359         newchar->subject_to_gravity     = 1;
7360         newchar->subject_to_minz        = 1;
7361         newchar->subject_to_maxz        = 1;
7362         newchar->no_adjust_base         = 0;
7363     }
7364     else if(stricmp(value, "text") == 0)   // Used for displaying text/images and freezing the screen
7365     {
7366         newchar->type                   = TYPE_TEXTBOX;
7367         newchar->subject_to_gravity     = 0;
7368         newchar->subject_to_minz        = 1;
7369         newchar->subject_to_maxz        = 1;
7370     }
7371     else if(stricmp(value, "endlevel") == 0)   // Used for ending the level when the players reach a certain point
7372     {
7373         newchar->type                   = TYPE_ENDLEVEL;
7374         newchar->subject_to_basemap     = 1;
7375         newchar->subject_to_wall        = 1;
7376         newchar->subject_to_platform    = 1;
7377         newchar->subject_to_hole        = 1;
7378         newchar->subject_to_obstacle    = 1;
7379         newchar->subject_to_gravity     = 1;
7380     }
7381     else if(stricmp(value, "npc") == 0)   // NPC type
7382     {
7383         newchar->type                   = TYPE_NPC;
7384         newchar->bounce                 = 1;
7385         newchar->subject_to_basemap     = 1;
7386         newchar->subject_to_wall        = 1;
7387         newchar->subject_to_platform    = 1;
7388         newchar->subject_to_hole        = 1;
7389         newchar->subject_to_obstacle    = 1;
7390         newchar->subject_to_gravity     = 1;
7391         newchar->subject_to_minz        = 1;
7392         newchar->subject_to_maxz        = 1;
7393         newchar->no_adjust_base         = 0;
7394     }
7395     else if(stricmp(value, "panel") == 0)   // NPC type
7396     {
7397         newchar->type                   = TYPE_PANEL;
7398         newchar->antigravity            = 1.0; //float type
7399         newchar->subject_to_gravity     = 1;
7400         newchar->no_adjust_base         = 1;
7401     }
7402     else
7403     {
7404         shutdown(1, "Model '%s' has invalid type: '%s'", filename, value);
7405     }
7406 }
7407 
lcmHandleCommandSubtype(ArgList * arglist,s_model * newchar,char * filename)7408 void lcmHandleCommandSubtype(ArgList *arglist, s_model *newchar, char *filename)
7409 {
7410     char *value = GET_ARGP(1);
7411     int i;
7412     if(stricmp(value, "biker") == 0)
7413     {
7414         newchar->subtype                                        = SUBTYPE_BIKER;
7415         if(newchar->aimove == -1)
7416         {
7417             newchar->aimove                 = 0;
7418         }
7419         newchar->aimove |= AIMOVE1_BIKER;
7420         if(!newchar->offscreenkill)
7421         {
7422             newchar->offscreenkill = 300;
7423         }
7424         for(i = 0; i < max_attack_types; i++)
7425         {
7426             newchar->defense[i].factor = 2.f;
7427         }
7428         newchar->subject_to_hole                                = 1;
7429         newchar->subject_to_gravity                             = 1;
7430         newchar->subject_to_basemap                             = 0;
7431         newchar->subject_to_wall                                = 0;
7432         newchar->subject_to_platform                            = 0;
7433         newchar->subject_to_screen                              = 0;
7434         newchar->subject_to_minz                                = 1;
7435         newchar->subject_to_maxz                                = 1;
7436         newchar->no_adjust_base                                 = 0;
7437     }
7438     else if(stricmp(value, "arrow") == 0) // 7-1-2005 Arrow type
7439     {
7440         newchar->subtype = SUBTYPE_ARROW;   // 7-1-2005 Arrow type
7441         if(newchar->aimove == -1)
7442         {
7443             newchar->aimove = 0;
7444         }
7445         newchar->aimove |= AIMOVE1_ARROW;
7446         if(!newchar->offscreenkill)
7447         {
7448             newchar->offscreenkill = 200;
7449         }
7450         newchar->subject_to_hole        = 0;
7451         newchar->subject_to_gravity     = 1;
7452         newchar->subject_to_basemap     = 0;
7453         newchar->subject_to_wall        = 0;
7454         newchar->subject_to_platform    = 0;
7455         newchar->subject_to_screen      = 0;
7456         newchar->subject_to_minz        = 1;
7457         newchar->subject_to_maxz        = 1;
7458         newchar->no_adjust_base         = 1;
7459     }
7460     else if(stricmp(value, "boomerang") == 0) // 16-12-2016 Boomrang type
7461     {
7462         newchar->subtype = SUBTYPE_BOOMERANG;   // 16-12-2016 Boomrang type
7463         if(newchar->aimove == -1)
7464         {
7465             newchar->aimove = 0;
7466         }
7467         newchar->aimove |= AIMOVE1_BOOMERANG;
7468         if(!newchar->offscreenkill)
7469         {
7470             newchar->offscreenkill = 200;
7471         }
7472         newchar->subject_to_hole        = 0;
7473         newchar->subject_to_gravity     = 1;
7474         newchar->subject_to_basemap     = 0;
7475         newchar->subject_to_wall        = 0;
7476         newchar->subject_to_platform    = 0;
7477         newchar->subject_to_screen      = 0;
7478         newchar->subject_to_minz        = 1;
7479         newchar->subject_to_maxz        = 1;
7480         newchar->no_adjust_base         = 1;
7481     }
7482     else if(stricmp(value, "notgrab") == 0)
7483     {
7484         newchar->subtype = SUBTYPE_NOTGRAB;
7485     }
7486     //    ltb 1-18-05  Item Subtype
7487     else if(stricmp(value, "touch") == 0)
7488     {
7489         newchar->subtype = SUBTYPE_TOUCH;
7490     }
7491     else if(stricmp(value, "weapon") == 0)
7492     {
7493         newchar->subtype = SUBTYPE_WEAPON;
7494     }
7495     else if(stricmp(value, "noskip") == 0)   // Text animation cannot be skipped if subtype noskip
7496     {
7497         newchar->subtype = SUBTYPE_NOSKIP;
7498     }
7499     else if(stricmp(value, "flydie") == 0)   // Obstacle will fly across the screen when hit if subtype flydie
7500     {
7501         newchar->subtype = SUBTYPE_FLYDIE;
7502     }
7503     else if(stricmp(value, "both") == 0)
7504     {
7505         newchar->subtype = SUBTYPE_BOTH;
7506     }
7507     else if(stricmp(value, "project") == 0)
7508     {
7509         newchar->subtype = SUBTYPE_PROJECTILE;
7510     }
7511     else if(stricmp(value, "follow") == 0)
7512     {
7513         newchar->subtype = SUBTYPE_FOLLOW;
7514     }
7515     else if(stricmp(value, "chase") == 0)
7516     {
7517         newchar->subtype = SUBTYPE_CHASE;
7518     }
7519     //    end new subtype
7520     else
7521     {
7522         shutdown(1, "Model '%s' has invalid subtype: '%s'", filename, value);
7523     }
7524 }
7525 
lcmHandleCommandSmartbomb(ArgList * arglist,s_model * newchar,char * filename)7526 void lcmHandleCommandSmartbomb(ArgList *arglist, s_model *newchar, char *filename)
7527 {
7528     //smartbomb now use a normal attack box
7529     if(!newchar->smartbomb)
7530     {
7531         newchar->smartbomb = malloc(sizeof(*newchar->smartbomb));
7532         *(newchar->smartbomb) = emptyattack;
7533     }
7534     else
7535     {
7536         shutdown(1, "Model '%s' has multiple smartbomb commands defined.", filename);
7537     }
7538 
7539     newchar->smartbomb->attack_force = atoi(GET_ARGP(1));			// Special force
7540     newchar->smartbomb->attack_type = atoi(GET_ARGP(2));			// Special attack type
7541     newchar->smartbomb->attack_drop = 1; //by default
7542     newchar->smartbomb->dropv.y = default_model_dropv.y;
7543 
7544     if(newchar->smartbomb->attack_type == ATK_BLAST)
7545     {
7546         newchar->smartbomb->blast = 1;
7547         newchar->smartbomb->dropv.x = default_model_dropv.x * 2.083f;
7548     }
7549     else
7550     {
7551         newchar->smartbomb->dropv.x = default_model_dropv.x;
7552     }
7553 
7554     if(newchar->smartbomb->attack_type == ATK_FREEZE)
7555     {
7556         newchar->smartbomb->freeze = 1;
7557         newchar->smartbomb->forcemap = -1;
7558         newchar->smartbomb->attack_drop = 0;
7559     }
7560     else if(newchar->smartbomb->attack_type == ATK_STEAL)
7561     {
7562         newchar->smartbomb->steal = 1;
7563     }
7564 
7565     if(newchar->type == TYPE_ITEM)
7566     {
7567         newchar->dofreeze = 0;								// Items don't animate
7568         newchar->smartbomb->freezetime = atoi(GET_ARGP(3)) * GAME_SPEED;
7569     }
7570     else
7571     {
7572         newchar->dofreeze = atoi(GET_ARGP(3));		// Are all animations frozen during special
7573         newchar->smartbomb->freezetime = atoi(GET_ARGP(4)) * GAME_SPEED;
7574     }
7575 }
7576 
lcmHandleCommandHostile(ArgList * arglist,s_model * newchar)7577 void lcmHandleCommandHostile(ArgList *arglist, s_model *newchar)
7578 {
7579     int i;
7580     char *value;
7581     newchar->hostile = 0;
7582 
7583     for(i = 1; (value = GET_ARGP(i)) && value[0]; i++)
7584     {
7585         if(stricmp(value, "enemy") == 0)
7586         {
7587             newchar->hostile |= TYPE_ENEMY;
7588         }
7589         else if(stricmp(value, "player") == 0)
7590         {
7591             newchar->hostile |= TYPE_PLAYER;
7592         }
7593         else if(stricmp(value, "obstacle") == 0)
7594         {
7595             newchar->hostile |= TYPE_OBSTACLE;
7596         }
7597         else if(stricmp(value, "shot") == 0)
7598         {
7599             newchar->hostile |= TYPE_SHOT;
7600         }
7601         else if(stricmp(value, "npc") == 0)
7602         {
7603             newchar->hostile |= TYPE_NPC;
7604         }
7605         else
7606         {
7607             newchar->hostile |= atoi(value); //debug raw integer value
7608         }
7609     }
7610 }
lcmHandleCommandCandamage(ArgList * arglist,s_model * newchar)7611 void lcmHandleCommandCandamage(ArgList *arglist, s_model *newchar)
7612 {
7613     int i;
7614     char *value;
7615     newchar->candamage = 0;
7616 
7617     for(i = 1; (value = GET_ARGP(i)) && value[0]; i++)
7618     {
7619         if(stricmp(value, "enemy") == 0)
7620         {
7621             newchar->candamage |= TYPE_ENEMY;
7622         }
7623         else if(stricmp(value, "player") == 0)
7624         {
7625             newchar->candamage |= TYPE_PLAYER;
7626         }
7627         else if(stricmp(value, "obstacle") == 0)
7628         {
7629             newchar->candamage |= TYPE_OBSTACLE;
7630         }
7631         else if(stricmp(value, "shot") == 0)
7632         {
7633             newchar->candamage |= TYPE_SHOT;
7634         }
7635         else if(stricmp(value, "npc") == 0)
7636         {
7637             newchar->candamage |= TYPE_NPC;
7638         }
7639         else if(stricmp(value, "ground") == 0)  // not really needed, though
7640         {
7641             newchar->ground = 1;
7642         }
7643         else
7644         {
7645             newchar->candamage |= atoi(value); //debug raw integer value
7646         }
7647     }
7648 }
7649 
lcmHandleCommandProjectilehit(ArgList * arglist,s_model * newchar)7650 void lcmHandleCommandProjectilehit(ArgList *arglist, s_model *newchar)
7651 {
7652     int i;
7653     char *value;
7654     newchar->projectilehit = 0;
7655 
7656     for(i = 1; (value = GET_ARGP(i)) && value[0]; i++)
7657     {
7658         if(stricmp(value, "enemy") == 0)
7659         {
7660             newchar->projectilehit |= TYPE_ENEMY;
7661         }
7662         else if(stricmp(value, "player") == 0)
7663         {
7664             newchar->projectilehit |= TYPE_PLAYER;
7665         }
7666         else if(stricmp(value, "obstacle") == 0)
7667         {
7668             newchar->projectilehit |= TYPE_OBSTACLE;
7669         }
7670         else if(stricmp(value, "shot") == 0)
7671         {
7672             newchar->projectilehit |= TYPE_SHOT;
7673         }
7674         else if(stricmp(value, "npc") == 0)
7675         {
7676             newchar->projectilehit |= TYPE_NPC;
7677         }
7678         else
7679         {
7680             newchar->projectilehit |= atoi(value); //debug raw integer value
7681         }
7682     }
7683 }
7684 
lcmHandleCommandAimove(ArgList * arglist,s_model * newchar,int * aimoveset,char * filename)7685 void lcmHandleCommandAimove(ArgList *arglist, s_model *newchar, int *aimoveset, char *filename)
7686 {
7687     char *value = GET_ARGP(1);
7688     if(!*aimoveset)
7689     {
7690         newchar->aimove = 0;
7691         *aimoveset = 1;
7692     }
7693 
7694     //main A.I. move switches
7695     if(value && value[0])
7696     {
7697         if(stricmp(value, "normal") == 0)
7698         {
7699             newchar->aimove |= AIMOVE1_NORMAL;
7700         }
7701         else if(stricmp(value, "chase") == 0)
7702         {
7703             newchar->aimove |= AIMOVE1_CHASE;
7704         }
7705         else if(stricmp(value, "chasex") == 0)
7706         {
7707             newchar->aimove |= AIMOVE1_CHASEX;
7708         }
7709         else if(stricmp(value, "chasez") == 0)
7710         {
7711             newchar->aimove |= AIMOVE1_CHASEZ;
7712         }
7713         else if(stricmp(value, "avoid") == 0)
7714         {
7715             newchar->aimove |= AIMOVE1_AVOID;
7716         }
7717         else if(stricmp(value, "avoidx") == 0)
7718         {
7719             newchar->aimove |= AIMOVE1_AVOIDX;
7720         }
7721         else if(stricmp(value, "avoidz") == 0)
7722         {
7723             newchar->aimove |= AIMOVE1_AVOIDZ;
7724         }
7725         else if(stricmp(value, "wander") == 0)
7726         {
7727             newchar->aimove |= AIMOVE1_WANDER;
7728         }
7729         else if(stricmp(value, "biker") == 0)
7730         {
7731             newchar->aimove |= AIMOVE1_BIKER;
7732         }
7733         else if(stricmp(value, "arrow") == 0)
7734         {
7735             newchar->aimove |= AIMOVE1_ARROW;
7736             if(!newchar->offscreenkill)
7737             {
7738                 newchar->offscreenkill = 200;
7739             }
7740         }
7741         else if(stricmp(value, "star") == 0)
7742         {
7743             newchar->aimove |= AIMOVE1_STAR;
7744         }
7745         else if(stricmp(value, "bomb") == 0)
7746         {
7747             newchar->aimove |= AIMOVE1_BOMB;
7748         }
7749         else if(stricmp(value, "nomove") == 0)
7750         {
7751             newchar->aimove |= AIMOVE1_NOMOVE;
7752         }
7753         else if(stricmp(value, "boomerang") == 0)
7754         {
7755             newchar->aimove |= AIMOVE1_BOOMERANG;
7756         }
7757         else
7758         {
7759             shutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename, value);
7760         }
7761     }
7762     value = GET_ARGP(2);
7763     //sub A.I. move switches
7764     if(value && value[0])
7765     {
7766         if(stricmp(value, "normal") == 0)
7767         {
7768             newchar->aimove |= AIMOVE2_NORMAL;
7769         }
7770         else if(stricmp(value, "ignoreholes") == 0)
7771         {
7772             newchar->aimove |= AIMOVE2_IGNOREHOLES;
7773         }
7774         else if(stricmp(value, "notargetidle") == 0)
7775         {
7776             newchar->aimove |= AIMOVE2_NOTARGETIDLE;
7777         }
7778         else
7779         {
7780             shutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename, value);
7781         }
7782     }
7783 }
lcmHandleCommandAiattack(ArgList * arglist,s_model * newchar,int * aiattackset,char * filename)7784 void lcmHandleCommandAiattack(ArgList *arglist, s_model *newchar, int *aiattackset, char *filename)
7785 {
7786     char *value = GET_ARGP(1);
7787     if(!*aiattackset)
7788     {
7789         newchar->aiattack = 0;
7790         *aiattackset = 1;
7791     }
7792 
7793     //main A.I. move switches
7794     if(value && value[0])
7795     {
7796         if(stricmp(value, "normal") == 0)
7797         {
7798             newchar->aiattack |= AIATTACK1_NORMAL;
7799         }
7800         else if(stricmp(value, "always") == 0)
7801         {
7802             newchar->aiattack |= AIATTACK1_ALWAYS;
7803         }
7804         else if(stricmp(value, "noattack") == 0)
7805         {
7806             newchar->aiattack |= AIATTACK1_NOATTACK;
7807         }
7808         else
7809         {
7810             printf("Model '%s' has invalid A.I. attack switch: '%s'\n", filename, value);
7811         }
7812     }
7813     /*
7814     value = GET_ARGP(2);
7815     //sub A.I. move switches
7816     if(value && value[0])
7817     {
7818 
7819     }*/
7820 }
7821 
lcmHandleCommandWeapons(ArgList * arglist,s_model * newchar)7822 void lcmHandleCommandWeapons(ArgList *arglist, s_model *newchar)
7823 {
7824     int weap;
7825     char *value;
7826     for(weap = 0; ; weap++)
7827     {
7828         value = GET_ARGP(weap + 1);
7829         if(!value[0])
7830         {
7831             break;
7832         }
7833     }
7834 
7835     if(!weap)
7836     {
7837         return;
7838     }
7839 
7840     newchar->numweapons = weap;
7841 
7842     if(!newchar->weapon)
7843     {
7844         newchar->weapon = malloc(sizeof(*newchar->weapon) * newchar->numweapons);
7845         memset(newchar->weapon, 0xFF, sizeof(*newchar->weapon)*newchar->numweapons);
7846         newchar->ownweapons = 1;
7847     }
7848     for(weap = 0; weap < newchar->numweapons ; weap++)
7849     {
7850         value = GET_ARGP(weap + 1);
7851         if(stricmp(value, "none") != 0)
7852         {
7853             newchar->weapon[weap] = get_cached_model_index(value);
7854         }
7855         else     // make empty weapon slots  2007-2-16
7856         {
7857             newchar->weapon[weap] = -1;
7858         }
7859     }
7860 }
7861 
7862 //fetch string between next @script and @end_script
7863 //return end position of @end_script, count from current position
fetchInlineScript(char * buf,char ** scriptbuf,ptrdiff_t * ppos,size_t * plen)7864 static size_t fetchInlineScript(char *buf, char **scriptbuf, ptrdiff_t *ppos, size_t *plen)
7865 {
7866     ptrdiff_t pos = *ppos;
7867     size_t len;
7868     while(!starts_with(buf + pos, "@script"))
7869     {
7870         pos++;
7871     }
7872     pos += strclen("@script");
7873     len = 0;
7874     while(!starts_with(buf + pos, "@end_script"))
7875     {
7876         len++;
7877         pos++;
7878     }
7879     *scriptbuf = malloc(sizeof(**scriptbuf) * len + 1);
7880     strncpy(*scriptbuf, buf + pos - len, len);
7881     (*scriptbuf)[len] = 0;
7882     pos += strclen("@end_script");
7883 
7884     *ppos = pos;
7885     *plen = len;
7886     return pos;
7887 }
7888 
lcmHandleCommandScripts(ArgList * arglist,char * buf,Script * script,char * scriptname,char * filename,int compile,int first)7889 size_t lcmHandleCommandScripts(ArgList *arglist, char *buf, Script *script, char *scriptname, char *filename, int compile, int first)
7890 {
7891     ptrdiff_t pos = 0;
7892     size_t len = 0;
7893     int result = 0;
7894     char *scriptbuf = NULL;
7895     Script_Init(script, scriptname, filename, first);
7896     if(stricmp(GET_ARGP(1), "@script") == 0)
7897     {
7898         fetchInlineScript(buf, &scriptbuf, &pos, &len);
7899         if(scriptbuf)
7900         {
7901             result = Script_AppendText(script, scriptbuf, filename);
7902             free(scriptbuf);
7903         }
7904     }
7905     else
7906     {
7907         result = load_script(script, GET_ARGP(1));
7908     }
7909     if(result)
7910     {
7911         if(compile)
7912         {
7913             Script_Compile(script);
7914         }
7915     }
7916     else
7917     {
7918         shutdown(1, "Unable to load %s '%s' in file '%s'.\n", scriptname, GET_ARGP(1), filename);
7919     }
7920     return pos;
7921 }
7922 
lcmScriptAvoidComment(char * buf,size_t buf_len,ptrdiff_t pos)7923 ptrdiff_t lcmScriptAvoidComment(char *buf, size_t buf_len, ptrdiff_t pos)
7924 {
7925     if ( buf && buf[0] )
7926     {
7927         unsigned char c = '\0';
7928         ptrdiff_t pre_pos = pos;
7929 
7930         c = buf[pos];
7931         if (c == '/')
7932         {
7933             ++pos;
7934             if ( pos < buf_len-1 )
7935             {
7936                 c = buf[pos];
7937                 if (c == '/')
7938                 {
7939                     while( pos < buf_len && c != 0x0D && c != 0x0A )
7940                     {
7941                         ++pos;
7942                         c = buf[pos];
7943                     }
7944                     if (pos < buf_len) ++pos;
7945                     pos = lcmScriptAvoidComment(buf,buf_len,pos);
7946                     return pos;
7947                 }
7948                 else if (c == '*')
7949                 {
7950                     ++pos;
7951                     if ( pos >= buf_len ) return pos;
7952                     c = buf[pos];
7953                     while( pos+1 < buf_len )
7954                     {
7955                         if ( c == '*' )
7956                         {
7957                             unsigned char c2 = buf[pos+1];
7958 
7959                             if ( c2 == '/' )
7960                             {
7961                                 pos += 2;
7962                                 pos = lcmScriptAvoidComment(buf,buf_len,pos);
7963                                 return pos;
7964                             }
7965                         }
7966                         ++pos;
7967                         c = buf[pos];
7968                     }
7969                 }
7970                 else
7971                 {
7972                     return pre_pos;
7973                 }
7974             }
7975         } else return pre_pos;
7976     }
7977 
7978     return pos;
7979 }
7980 
lcmScriptGetMainPos(char * buf,size_t * buf_len)7981 ptrdiff_t lcmScriptGetMainPos(char *buf, size_t *buf_len)
7982 {
7983     if ( buf && buf[0] )
7984     {
7985         size_t len = *buf_len;
7986         ptrdiff_t pos = 0;
7987         enum {START,PRE0,PRE1,PRE2,PRE3,M0,M1,M2,M3,P0,P1,END} current_state = START;
7988         unsigned char c = '\0';
7989         int index_res = -1;
7990 
7991         pos = lcmScriptAvoidComment(buf,len,pos);
7992         c = (unsigned char)tolower((int)buf[pos]);
7993         while ( pos < len && current_state != END )
7994         {
7995             switch(current_state)
7996             {
7997                 case START:
7998                 {
7999                     if ( c == 'v' )
8000                     {
8001                         index_res = pos; // store void main() start pos
8002                         current_state = PRE0;
8003                     }
8004                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = START;
8005                     else if ( c == '\0' ) return -1;
8006                     else current_state = START;
8007                     break;
8008                 }
8009                 case PRE0:
8010                 {
8011                     if ( c == 'o' ) current_state = PRE1;
8012                     else if ( c == '\0' ) return -1;
8013                     else current_state = START;
8014                     break;
8015                 }
8016                 case PRE1:
8017                 {
8018                     if ( c == 'i' ) current_state = PRE2;
8019                     else if ( c == '\0' ) return -1;
8020                     else current_state = START;
8021                     break;
8022                 }
8023                 case PRE2:
8024                 {
8025                     if ( c == 'd' ) current_state = PRE3;
8026                     else if ( c == '\0' ) return -1;
8027                     else current_state = START;
8028                     break;
8029                 }
8030                 case PRE3:
8031                 {
8032                     if ( c == 'm' ) current_state = M0;
8033                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = PRE3;
8034                     else if ( c == '\0' ) return -1;
8035                     else current_state = START;
8036                     break;
8037                 }
8038                 case M0:
8039                 {
8040                     if ( c == 'a' ) current_state = M1;
8041                     else if ( c == '\0' ) return -1;
8042                     else current_state = START;
8043                     break;
8044                 }
8045                 case M1:
8046                 {
8047                     if ( c == 'i' ) current_state = M2;
8048                     else if ( c == '\0' ) return -1;
8049                     else current_state = START;
8050                     break;
8051                 }
8052                 case M2:
8053                 {
8054                     if ( c == 'n' ) current_state = M3;
8055                     else if ( c == '\0' ) return -1;
8056                     else current_state = START;
8057                     break;
8058                 }
8059                 case M3:
8060                 {
8061                     if ( c == '(' ) current_state = P0;
8062                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = M3;
8063                     else if ( c == '\0' ) return -1;
8064                     else current_state = START;
8065                     break;
8066                 }
8067                 case P0:
8068                 {
8069                     if ( c == ')' ) current_state = P1;
8070                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = P0;
8071                     else if ( c == '\0' ) return -1;
8072                     else current_state = START;
8073                     break;
8074                 }
8075                 case P1:
8076                 {
8077                     if ( c == '{' )
8078                     {
8079                         current_state = END;
8080                         if (++pos < len)
8081                         {
8082                             *buf_len = pos-index_res;
8083                             return index_res;
8084                         }
8085                         else return -1;
8086                     }
8087                     else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = P1;
8088                     else if ( c == '\0' ) return -1;
8089                     else current_state = START;
8090                     break;
8091                 }
8092                 case END:
8093                 default:
8094                     break;
8095             }
8096             ++pos; // at the end position after the '{'
8097             if (current_state == END)
8098             {
8099                 if (pos < len)
8100                 {
8101                     *buf_len = pos-index_res;
8102                     return index_res;
8103                 }
8104                 else return -1;
8105             }
8106             pos = lcmScriptAvoidComment(buf,len,pos);
8107             c = (unsigned char)tolower((int)buf[pos]);
8108         } // end while loop
8109 
8110         if (current_state == END)
8111         {
8112             if (pos < len)
8113             {
8114                 *buf_len = pos-index_res;
8115                 return index_res;
8116             }
8117             else return -1;
8118         } else return -1;
8119     }
8120 
8121     return -1;
8122 }
8123 
8124 // add main (or add vars in main) - used for animationscript file
lcmScriptAddMain(char ** buf)8125 size_t lcmScriptAddMain(char **buf)
8126 {
8127     size_t len = 0, len2 = 0, buf_len = 0;
8128     ptrdiff_t pos = 0;
8129     char* newbuf = NULL;
8130 
8131     if ( (*buf) && (*buf)[0] )
8132     {
8133         buf_len = len = strlen(*buf);
8134 
8135         pos = lcmScriptGetMainPos(*buf,&buf_len);
8136         if ( pos == -1 ) // main() not found!
8137         {
8138             char mtxt[] = "\n\nvoid main()\n{\n    int frame = getlocalvar(\"frame\");\n    int animhandle = getlocalvar(\"animhandle\");\n\n}\n\n";
8139 
8140             pos += buf_len;
8141             pos = len; // pos before '\0' (at last char)
8142             len2 = strlen(mtxt);
8143             newbuf = malloc(sizeof(**buf)*len + sizeof(mtxt)*len2 + 1 );
8144             strncpy(newbuf, *buf, pos);
8145             strncpy(newbuf+pos, mtxt, len2);
8146             newbuf[len+len2] = '\0';
8147 
8148             free( (*buf) );
8149             (*buf) = newbuf;
8150 
8151             //printf("written main in buffer\n%s\n",newbuf);
8152         }
8153         else
8154         {
8155             int pos2 = 0;
8156             char mtxt[] = "\n\nvoid main()\n{\n    int frame = getlocalvar(\"frame\");\n    int animhandle = getlocalvar(\"animhandle\");\n\n";
8157 
8158             len2 = strlen(mtxt);
8159             pos2 = pos + buf_len;
8160 
8161             newbuf = malloc(sizeof(**buf)*pos + sizeof(mtxt)*len2 + sizeof(**buf)*(len-pos2) + 1 );
8162             strncpy(newbuf, *buf, pos);
8163             strncpy(newbuf+pos, mtxt, len2);
8164             strncpy(newbuf+pos+len2, *buf+pos2, len-pos2);
8165             newbuf[pos+len2+len-pos2] = '\0';
8166 
8167             free( (*buf) );
8168             (*buf) = newbuf;
8169 
8170             //printf("search for main in buffer\n%s pos:%d\n",*buf,pos);
8171         }
8172 
8173         //printf("search for main in buffer\n%s pos:%d\n",*buf,pos);
8174     }
8175 
8176     return len;
8177 }
8178 
8179 // used to join animationscript @scrip/@cmd width animationscript file main()
lcmScriptJoinMain(char ** buf,char * first_buf)8180 size_t lcmScriptJoinMain(char **buf, char *first_buf)
8181 {
8182     size_t len = 0, len2 = 0, buf_len = 0;
8183     ptrdiff_t pos = 0;
8184     int pcount = 0;
8185     char* newbuf = NULL;
8186 
8187     if ( (*buf) && first_buf && (*buf)[0] && first_buf[0] )
8188     {
8189         buf_len = len = strlen(*buf);
8190         len2 = strlen(first_buf);
8191 
8192         pos = lcmScriptGetMainPos(*buf,&buf_len);
8193         pos += buf_len;
8194 
8195         pcount = 1;
8196         while(pcount > 0)
8197         {
8198             if( (*buf)[pos] == '{' ) ++pcount;
8199             else if( (*buf)[pos] == '}' ) --pcount;
8200             pos++;
8201         }
8202         --pos; // back to last '}' of main()
8203 
8204         newbuf = malloc(sizeof(**buf)*len + sizeof(*first_buf)*len2 + 1 );
8205         strncpy(newbuf, *buf, pos);
8206         strncpy(newbuf+pos, first_buf, len2);
8207         strncpy(newbuf+pos+len2, *buf+pos, len-pos);
8208         newbuf[len+len2] = '\0';
8209 
8210         free( (*buf) );
8211         (*buf) = newbuf;
8212 
8213         //printf("test joined buffer\n%s\n",*buf);
8214     }
8215 
8216     return len;
8217 }
8218 
lcmScriptCopyBuffer(ArgList * arglist,char * buf,char ** script_buffer)8219 size_t lcmScriptCopyBuffer(ArgList *arglist, char *buf, char **script_buffer)
8220 {
8221     ptrdiff_t pos = 0;
8222     size_t len = 0;
8223 
8224     if(stricmp(GET_ARGP(1), "@script") == 0)
8225     {
8226         fetchInlineScript(buf, script_buffer, &pos, &len);
8227     }
8228     else
8229     {
8230         buffer_pakfile(GET_ARGP(1), script_buffer, &len);
8231     }
8232 
8233     //printf("test buffer\n%s\n",*script_buffer);
8234     return pos;
8235 }
8236 
lcmScriptDeleteMain(char ** buf)8237 size_t lcmScriptDeleteMain(char **buf)
8238 {
8239     size_t len = 0;
8240     long i = 0;
8241     ptrdiff_t pos = 0;
8242     char *newbuf = NULL;
8243 
8244     if ((*buf) && (*buf)[0])
8245     {
8246         len = strlen(*buf);
8247 
8248         while(!starts_with((*buf) + pos, "main()")) pos++;
8249         pos += strclen("main()");
8250         while(!starts_with(*buf + pos, "{")) pos++;
8251         pos += strclen("{");
8252         while(!starts_with(*buf + pos, "int frame = getlocalvar(\"frame\");")) pos++;
8253         pos += strclen("int frame = getlocalvar(\"frame\");");
8254         while(!starts_with(*buf + pos, "int animhandle = getlocalvar(\"animhandle\");\n")) pos++;
8255         pos += strclen("int animhandle = getlocalvar(\"animhandle\");\n");
8256 
8257         for(i = len-1; i >= 0; i--)
8258         {
8259             if ( (*buf)[i] == '}' )
8260             {
8261                 len = i;
8262                 break;
8263             } else continue;
8264         }
8265 
8266         len = len-pos;
8267         newbuf = malloc(sizeof(newbuf) * (len) + 1);
8268         strncpy(newbuf, *buf+pos, len);
8269         newbuf[len] = '\0';
8270 
8271         free( (*buf) );
8272         (*buf) = newbuf;
8273 
8274         //printf("test delete main\n%s\n",newbuf);
8275     }
8276 
8277     return len;
8278 }
8279 
8280 //alloc a new model, and everything thats required,
8281 //set all values to defaults
init_model(int cacheindex,int unload)8282 s_model *init_model(int cacheindex, int unload)
8283 {
8284     //to free: newchar, newchar->offense_factors, newchar->special, newchar->animation - OK
8285     int i;
8286 
8287     s_model *newchar = calloc(1, sizeof(*newchar));
8288     if(!newchar)
8289     {
8290         shutdown(1, (char *)E_OUT_OF_MEMORY);
8291     }
8292     newchar->name = model_cache[cacheindex].name; // well give it a name for sort method
8293     newchar->index = cacheindex;
8294     newchar->isSubclassed = 0;
8295     newchar->freetypes = MF_ALL;
8296 
8297     newchar->priority = 1;
8298 
8299     newchar->defense		        = calloc(max_attack_types + 1, sizeof(*newchar->defense));
8300     newchar->offense_factors        = calloc(max_attack_types + 1, sizeof(*newchar->offense_factors));
8301 
8302     newchar->special                = calloc(1, sizeof(*newchar->special));
8303 
8304     alloc_all_scripts(&newchar->scripts);
8305 
8306     newchar->unload             = unload;
8307     newchar->jumpspeed          = default_model_jumpspeed;
8308     newchar->jumpheight         = default_model_jumpheight; // 28-12-2004   Set default jump height to 4, if not specified
8309     newchar->runjumpheight      = default_model_jumpheight; // Default jump height if a player is running
8310     newchar->runjumpdist        = 1; // Default jump distane if a player is running
8311     newchar->grabdistance       = default_model_grabdistance; //  30-12-2004 Default grabdistance is same as originally set
8312     newchar->grabflip		    = 3;
8313     newchar->throwdamage        = 21; // default throw damage
8314     newchar->icon.def			= -1;
8315     newchar->icon.die           = -1;
8316     newchar->icon.pain          = -1;
8317     newchar->icon.get           = -1;
8318     newchar->icon.weapon		= -1;			    // No weapon icon set yet
8319     newchar->diesound           = -1;
8320     newchar->nolife             = 0;			    // default show life = 1 (yes)
8321     newchar->remove             = 1;			    // Flag set to weapons are removed upon hitting an opponent
8322     newchar->throwdist          = default_model_jumpheight * 0.625f;
8323     newchar->counter            = 3;			    // Default 3 times to drop a weapon / projectile
8324     newchar->aimove             = -1;
8325     newchar->aiattack           = -1;
8326     newchar->throwframewait     = -1;               // makes sure throw animations run normally unless throwfram is specified, added by kbandressen 10/20/06
8327     newchar->path               = model_cache[cacheindex].path;         // Record path, so script can get it without looping the whole model collection.
8328     newchar->icon.mphigh        = -1;               //No mphigh icon yet.
8329     newchar->icon.mplow         = -1;               //No mplow icon yet.
8330     newchar->icon.mpmed         = -1;               //No mpmed icon yet.
8331 
8332     // Default Attack1 in chain must be referenced if not used.
8333     for(i = 0; i < MAX_ATCHAIN; i++)
8334     {
8335         newchar->atchain[i] = 1;
8336     }
8337     newchar->chainlength = 1;
8338 
8339     if(magic_type == 1)
8340     {
8341         newchar->mprate = 1;
8342     }
8343     else
8344     {
8345         newchar->mprate                = 2;
8346     }
8347     newchar->chargerate = newchar->guardrate = 2;
8348     newchar->risetime.rise              = -1;
8349     newchar->sleepwait                  = 1000;
8350     newchar->jugglepoints.current = newchar->jugglepoints.max = 0;
8351     newchar->guardpoints.current = newchar->guardpoints.max = 0;
8352     newchar->mpswitch                   = -1;       // switch between reduce mp or gain mp for mpstabletype 4
8353     newchar->weaploss[0]                = WEAPLOSS_TYPE_ANY;
8354     newchar->weaploss[1]                = -1;
8355     newchar->lifespan                   = 0x7fffffff;
8356     newchar->summonkill                 = 1;
8357     newchar->candamage                  = -1;
8358     newchar->hostile                    = -1;
8359     newchar->projectilehit              = -1;
8360     newchar->subject_to_basemap         = -1;
8361     newchar->subject_to_wall            = -1;
8362     newchar->subject_to_platform        = -1;
8363     newchar->subject_to_obstacle        = -1;
8364     newchar->subject_to_hole            = -1;
8365     newchar->subject_to_gravity         = -1;
8366     newchar->subject_to_screen          = -1;
8367     newchar->subject_to_minz            = -1;
8368     newchar->subject_to_maxz            = -1;
8369     newchar->no_adjust_base             = -1;
8370     newchar->pshotno                    = -1;
8371     newchar->project                    = -1;
8372     newchar->dust.fall_land             = -1;
8373     newchar->dust.jump_land             = -1;
8374     newchar->dust.jump_start            = -1;
8375     newchar->bomb                       = -1;
8376     newchar->star                       = -1;
8377     newchar->knife                      = -1;
8378     newchar->stealth.hide               = 0;
8379     newchar->stealth.detect             = 0;
8380     newchar->attackthrottle				= 0.0f;
8381     newchar->attackthrottletime			= noatk_duration * GAME_SPEED;
8382 
8383     newchar->animation = calloc(max_animations, sizeof(*newchar->animation));
8384     if(!newchar->animation)
8385     {
8386         shutdown(1, (char *)E_OUT_OF_MEMORY);
8387     }
8388 
8389     // default string value, only by reference
8390     newchar->rider = get_cached_model_index("K'");
8391     newchar->flash = newchar->bflash = get_cached_model_index("flash");
8392 
8393     //Default offense/defense values.
8394     for(i = 0; i < max_attack_types; i++)
8395     {
8396         newchar->offense_factors[i]     = 1;
8397         newchar->defense[i]				= default_defense;
8398     }
8399 
8400     //Default sight ranges.
8401     newchar->sight.min.x = -9999;
8402     newchar->sight.max.x = 9999;
8403     newchar->sight.min.y = -9999;
8404     newchar->sight.max.y = 9999;
8405     newchar->sight.min.z = -9999;
8406     newchar->sight.max.z = 9999;
8407 
8408     return newchar;
8409 }
8410 
update_model_loadflag(s_model * model,char unload)8411 void update_model_loadflag(s_model *model, char unload)
8412 {
8413     model->unload = unload;
8414 }
8415 
load_cached_model(char * name,char * owner,char unload)8416 s_model *load_cached_model(char *name, char *owner, char unload)
8417 {
8418 
8419 
8420     s_model *newchar = NULL,
8421             *tempmodel = NULL;
8422 
8423     s_anim *newanim = NULL;
8424 
8425     char *filename = NULL,
8426          *buf = NULL,
8427          *animscriptbuf = NULL,
8428          *scriptbuf = NULL,
8429          *command = NULL,
8430          *value = NULL,
8431          *value2 = NULL,
8432          *value3 = NULL;
8433 
8434     char fnbuf[128] = {""},
8435                       namebuf[256] = {""},
8436                                      argbuf[MAX_ARG_LEN + 1] = {""};
8437 
8438     ArgList arglist;
8439 
8440     float tempFloat;
8441 
8442     int ani_id = -1,
8443         script_id = -1,
8444         frm_id = -1,
8445         i = 0,
8446         j = 0,
8447         tempInt = 0,
8448         framecount = 0,
8449         frameset = 0,
8450         peek = 0,
8451         cacheindex = 0,
8452         curframe = 0,
8453         delay = 0,
8454         errorVal = 0,
8455         shadow_set = 0,
8456         idle = 0,
8457         frameshadow = -1,	// -1 will use default shadow for this entity, otherwise will use this value
8458         soundtoplay = -1,
8459         aimoveset = 0,
8460         aiattackset = 0,
8461         maskindex = -1,
8462         nopalette = 0;
8463 
8464     size_t size = 0,
8465            line = 0,
8466            len = 0,
8467            sbsize = 0,
8468            scriptlen = 0;
8469 
8470     ptrdiff_t pos = 0,
8471               index = 0;
8472 
8473     s_hitbox            bbox = {    .x      = 0,
8474                                     .y      = 0,
8475                                     .width  = 0,
8476                                     .height = 0,
8477                                     .z1     = 0,
8478                                     .z2     = 0};
8479 
8480     s_hitbox            abox = {    .x = 0,
8481                                     .y = 0,
8482                                     .width = 0,
8483                                     .height = 0,
8484                                     .z1 = 0,
8485                                     .z2 = 0};
8486 
8487     s_axis_i_2d         offset = {  .x = 0,
8488                                     .y = 0 };
8489     int                 shadow_xz[2] = {0, 0};
8490     int                 shadow_coords[2] = {0, 0};
8491 
8492     float               platform[8] = { 0, 0, 0, 0, 0, 0, 0, 0 },
8493                         platform_con[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
8494 
8495     s_axis_i            move = {    .base = -1,    //-1 = Disabled, 0+ base set
8496                                     .x = 0,
8497                                     .y = 0,
8498                                     .z = 0};
8499 
8500     s_damage_recursive  recursive;
8501     s_hitbox            attack_coords;
8502     s_collision_attack  attack;
8503     s_collision_attack  *pattack = NULL;
8504     s_collision_body    bbox_con;
8505     s_hitbox            body_coords;
8506     s_defense           defense;
8507     s_drawmethod        drawmethod;
8508     s_drawmethod        dm;
8509 
8510     char *shutdownmessage = NULL;
8511 
8512 
8513 
8514     unsigned *mapflag = NULL;  // in 24bit mode, we need to know whether a colourmap is a common map or a palette
8515 
8516     static const char pre_text[] =   // this is the skeleton of frame function
8517     {
8518         "void main()\n"
8519         "{\n"
8520         "    int frame = getlocalvar(\"frame\");\n"
8521         "    int animhandle = getlocalvar(\"animhandle\");\n"
8522         "\n}\n"
8523     };
8524 
8525     static const char sur_text[] =  // end of function text
8526     {
8527         "\n}\n"
8528     };
8529 
8530     static const char ifid_text[] =  // if expression to check animation id
8531     {
8532         "    if(animhandle==%d)\n"
8533         "    {\n"
8534         "        return;\n"
8535         "    }\n"
8536     };
8537 
8538     static const char endifid_text[] =  // end of if
8539     {
8540         "        return;\n"
8541         "    }\n"
8542     };
8543 
8544     static const char if_text[] =  // this is the if expression of frame function
8545     {
8546         "        if(frame==%d)\n"
8547         "        {\n"
8548     };
8549 
8550     static const char endif_return_text[] =   //return to reduce unecessary checks
8551     {
8552         "            return;\n"
8553     };
8554 
8555     static const char endif_text[] =  // end of if
8556     {
8557         "        }\n"
8558     } ;
8559 
8560     static const char comma_text[] =  // arguments separator
8561     {
8562         ", "
8563     };
8564 
8565     static const char call_text[] =  //begin of function call
8566     {
8567         "            %s("
8568     };
8569 
8570     static const char endcall_text[] =  //end of function call
8571     {
8572         ");\n"
8573     };
8574 
8575     modelCommands cmd;
8576     s_scripts *tempscripts;
8577 
8578 #ifdef DEBUG
8579     printf("load_cached_model: %s, unload: %d\n", name, unload);
8580 #endif
8581 
8582     // Model already loaded but we might want to unload after level is completed.
8583     if((tempmodel = findmodel(name)) != NULL)
8584     {
8585         update_model_loadflag(tempmodel, unload);
8586         cache_model_sprites(tempmodel, 1);
8587         return tempmodel;
8588     }
8589 
8590     cacheindex = get_cached_model_index(name);
8591     if(cacheindex < 0)
8592     {
8593         shutdown(1, "Fatal: No cache entry for '%s' within '%s'\n\n", name, owner);
8594     }
8595 
8596     filename = model_cache[cacheindex].path;
8597 
8598     if(buffer_pakfile(filename, &buf, &size) != 1)
8599     {
8600         shutdown(1, "Unable to open file '%s'\n\n", filename);
8601     }
8602 
8603     sbsize = size + 1;
8604     scriptbuf = (char *)malloc(sbsize);
8605 
8606     if(scriptbuf == NULL)
8607     {
8608         shutdown(1, "Unable to create script buffer for file '%s' (%i bytes)", filename, size * 2);
8609     }
8610     scriptbuf[0] = 0;
8611 
8612     //_peek_model_name(cacheindex);
8613     newchar = init_model(cacheindex, unload);
8614     //newchar->name = name;
8615 
8616     //attention, we increase models_loaded here, this can be dangerous if we access that value later on,
8617     //since recursive calls will change it!
8618     models_loaded++;
8619     addModel(newchar);
8620 
8621     attack = emptyattack;      // empty attack
8622     bbox_con = empty_body;
8623 
8624     drawmethod = plainmethod;  // better than memset it to 0
8625 
8626     newchar->hitwalltype = -1; // init to -1
8627 
8628 
8629     //char* test = "load   knife 0";
8630     //ParseArgs(&arglist,test,argbuf);
8631 
8632     // Now interpret the contents of buf line by line
8633     while(pos < size)
8634     {
8635         //command = GET_ARG(0);
8636         line++;
8637         if(ParseArgs(&arglist, buf + pos, argbuf))
8638         {
8639             command = GET_ARG(0);
8640             cmd = getModelCommand(modelcmdlist, command);
8641 
8642             switch(cmd)
8643             {
8644             case CMD_MODEL_BACKPAIN:
8645                 value = GET_ARG(1);
8646                 newchar->backpain = atoi(value);
8647                 break;
8648             case CMD_MODEL_SUBCLASS:
8649                 //inherit everything from an existing, cached model
8650                 tempmodel = findmodel(GET_ARG(1));
8651                 if (!tempmodel)
8652                 {
8653                     shutdownmessage = "tried to subclass a non-existing/not previously loaded model!";
8654                     goto lCleanup;
8655                 }
8656                 tempscripts = newchar->scripts;
8657                 *newchar = *tempmodel;
8658                 newchar->scripts = tempscripts;
8659                 copy_all_scripts(tempmodel->scripts, newchar->scripts, 1);
8660                 newchar->isSubclassed = 1;
8661                 newchar->freetypes = MF_SCRIPTS;
8662                 break;
8663             case CMD_MODEL_NAME:
8664                 lcmHandleCommandName(&arglist, newchar, cacheindex);
8665                 break;
8666             case CMD_MODEL_TYPE:
8667                 lcmHandleCommandType(&arglist, newchar, filename);
8668                 break;
8669             case CMD_MODEL_SUBTYPE:
8670                 lcmHandleCommandSubtype(&arglist, newchar, filename);
8671                 break;
8672             case CMD_MODEL_STATS:
8673                 value = GET_ARG(1);
8674                 newchar->stats[atoi(value)] = GET_FLOAT_ARG(2);
8675                 break;
8676             case CMD_MODEL_HEALTH:
8677                 value = GET_ARG(1);
8678                 newchar->health = atoi(value);
8679                 break;
8680             case CMD_MODEL_PRIORITY:
8681                 value = GET_ARG(1);
8682                 newchar->priority = atoi(value);
8683                 break;
8684             case CMD_MODEL_SCROLL:
8685                 value = GET_ARG(1);
8686                 newchar->scroll = atof(value);
8687                 break;
8688             case CMD_MODEL_MP: //Left for backward compatability. See mpset. // mp values to put max mp for player by tails
8689                 value = GET_ARG(1);
8690                 newchar->mp = atoi(value);
8691                 break;
8692             case CMD_MODEL_NOLIFE:	// Feb 25, 2005 - Flag to display enemy life or not
8693                 newchar->nolife = GET_INT_ARG(1);
8694                 break;
8695             case CMD_MODEL_MAKEINV:	// Mar 12, 2005 - If a value is supplied, corresponds to amount of time the player spawns invincible
8696                 newchar->makeinv = GET_FLOAT_ARG(1) * GAME_SPEED;
8697                 if(GET_INT_ARG(2))
8698                 {
8699                     newchar->makeinv = -newchar->makeinv;
8700                 }
8701                 break;
8702             case CMD_MODEL_RISEINV:
8703                 newchar->riseinv = GET_FLOAT_ARG(1) * GAME_SPEED;
8704                 if(GET_INT_ARG(2))
8705                 {
8706                     newchar->riseinv = -newchar->riseinv;
8707                 }
8708                 break;
8709             case CMD_MODEL_LOAD:
8710                 value = GET_ARG(1);
8711                 tempmodel = findmodel(value);
8712                 if(!tempmodel)
8713                 {
8714                     load_cached_model(value, name, GET_INT_ARG(2));
8715                 }
8716                 else
8717                 {
8718                     update_model_loadflag(tempmodel, GET_INT_ARG(2));
8719                 }
8720                 break;
8721             case CMD_MODEL_SCORE:
8722                 newchar->score = GET_INT_ARG(1);
8723                 newchar->multiple = GET_INT_ARG(2);			// New var multiple for force/scoring
8724                 break;
8725             case CMD_MODEL_SMARTBOMB:
8726                 lcmHandleCommandSmartbomb(&arglist, newchar, filename);
8727                 break;
8728             case CMD_MODEL_BOUNCE:
8729                 newchar->bounce = GET_INT_ARG(1);
8730                 break;
8731             case CMD_MODEL_NOQUAKE:  // Mar 12, 2005 - Flag to determine if entity shakes screen
8732                 newchar->noquake |= GET_INT_ARG(1) ? NO_QUAKE : 0;
8733                 newchar->noquake |= GET_INT_ARG(2) ? NO_QUAKEN : 0;
8734                 break;
8735             case CMD_MODEL_BLOCKBACK:	// Flag to determine if attacks can be blocked from behind
8736                 newchar->blockback = GET_INT_ARG(1);
8737                 break;
8738             case CMD_MODEL_HITENEMY:	// Flag to determine if an enemy projectile will hit enemies
8739                 value = GET_ARG(1);
8740                 if(atoi(value) == 1)
8741                 {
8742                     newchar->candamage = newchar->hostile = TYPE_PLAYER | TYPE_ENEMY;
8743                 }
8744                 else if(atoi(value) == 2)
8745                 {
8746                     newchar->candamage = newchar->hostile = TYPE_PLAYER;
8747                 }
8748                 newchar->ground = GET_INT_ARG(2);    // Added to determine if enemies are damaged with mid air projectiles or ground only
8749                 break;
8750             case CMD_MODEL_HOSTILE:
8751                 lcmHandleCommandHostile(&arglist, newchar);
8752                 break;
8753             case CMD_MODEL_CANDAMAGE:
8754                 lcmHandleCommandCandamage(&arglist, newchar);
8755                 break;
8756             case CMD_MODEL_PROJECTILEHIT:
8757                 lcmHandleCommandProjectilehit(&arglist, newchar);
8758                 break;
8759             case CMD_MODEL_AIMOVE:
8760                 lcmHandleCommandAimove(&arglist, newchar, &aimoveset, filename);
8761                 break;
8762             case CMD_MODEL_AIATTACK:
8763                 lcmHandleCommandAiattack(&arglist, newchar, &aiattackset, filename);
8764                 break;
8765             case CMD_MODEL_SUBJECT_TO_BASEMAP:
8766                 newchar->subject_to_basemap = (0 != GET_INT_ARG(1));
8767                 break;
8768             case CMD_MODEL_SUBJECT_TO_WALL:
8769                 newchar->subject_to_wall = (0 != GET_INT_ARG(1));
8770                 break;
8771             case CMD_MODEL_SUBJECT_TO_HOLE:
8772                 newchar->subject_to_hole = (0 != GET_INT_ARG(1));
8773                 break;
8774             case CMD_MODEL_SUBJECT_TO_PLATFORM:
8775                 newchar->subject_to_platform = (0 != GET_INT_ARG(1));
8776                 break;
8777             case CMD_MODEL_SUBJECT_TO_OBSTACLE:
8778                 newchar->subject_to_obstacle = (0 != GET_INT_ARG(1));
8779                 break;
8780             case CMD_MODEL_SUBJECT_TO_GRAVITY:
8781                 newchar->subject_to_gravity = (0 != GET_INT_ARG(1));
8782                 break;
8783             case CMD_MODEL_SUBJECT_TO_SCREEN:
8784                 newchar->subject_to_screen = (0 != GET_INT_ARG(1));
8785                 break;
8786             case CMD_MODEL_SUBJECT_TO_MINZ:
8787                 newchar->subject_to_minz = (0 != GET_INT_ARG(1));
8788                 break;
8789             case CMD_MODEL_SUBJECT_TO_MAXZ:
8790                 newchar->subject_to_maxz = (0 != GET_INT_ARG(1));
8791                 break;
8792             case CMD_MODEL_NO_ADJUST_BASE:
8793                 newchar->no_adjust_base = (0 != GET_INT_ARG(1));
8794                 break;
8795             case CMD_MODEL_INSTANTITEMDEATH:
8796                 newchar->instantitemdeath = GET_INT_ARG(1);
8797                 break;
8798             case CMD_MODEL_SECRET:
8799                 newchar->secret = GET_INT_ARG(1);
8800                 newchar->clearcount = GET_INT_ARG(2);
8801                 break;
8802             case CMD_MODEL_MODELFLAG: //model copy flag
8803                 newchar->model_flag = GET_INT_ARG(1);
8804                 break;
8805                 // weapons
8806             case CMD_MODEL_WEAPLOSS:
8807                 newchar->weaploss[0] = GET_INT_ARG(1);
8808                 newchar->weaploss[1] = GET_INT_ARG(2);
8809                 break;
8810             case CMD_MODEL_WEAPNUM:
8811                 newchar->weapnum = GET_INT_ARG(1);
8812                 break;
8813             case CMD_MODEL_PROJECT: // New projectile subtype
8814                 value = GET_ARG(1);
8815                 if(stricmp(value, "none") == 0)
8816                 {
8817                     newchar->project = -1;
8818                 }
8819                 else
8820                 {
8821                     newchar->project = get_cached_model_index(value);
8822                 }
8823                 break;
8824             case CMD_MODEL_WEAPONS:
8825                 lcmHandleCommandWeapons(&arglist, newchar);
8826                 break;
8827             case CMD_MODEL_SHOOTNUM: //here weapons things like shoot rest type of weapon ect..by tails
8828                 newchar->shootnum = GET_INT_ARG(1);
8829                 break;
8830             case CMD_MODEL_RELOAD:
8831                 newchar->reload = GET_INT_ARG(1);
8832                 break;
8833             case CMD_MODEL_TYPESHOT:
8834                 newchar->typeshot = GET_INT_ARG(1);
8835                 break;
8836             case CMD_MODEL_COUNTER:
8837                 newchar->counter = GET_INT_ARG(1);
8838                 break;
8839             case CMD_MODEL_ANIMAL:
8840                 newchar->animal = GET_INT_ARG(1);
8841                 break;
8842             case CMD_MODEL_RIDER:
8843                 value = GET_ARG(1);
8844                 if(stricmp(value, "none") == 0)
8845                 {
8846                     newchar->rider = -1;
8847                 }
8848                 else
8849                 {
8850                     newchar->rider = get_cached_model_index(value);
8851                 }
8852                 break;
8853             case CMD_MODEL_KNIFE:
8854             case CMD_MODEL_FIREB:
8855             case CMD_MODEL_PLAYSHOT:
8856             case CMD_MODEL_PLAYSHOTW:
8857                 value = GET_ARG(1);
8858                 if(stricmp(value, "none") == 0)
8859                 {
8860                     newchar->knife = -1;
8861                 }
8862                 else
8863                 {
8864                     newchar->knife = get_cached_model_index(value);
8865                 }
8866                 break;
8867             case CMD_MODEL_PLAYSHOTNO:
8868                 value = GET_ARG(1);
8869                 if(stricmp(value, "none") == 0)
8870                 {
8871                     newchar->pshotno = -1;
8872                 }
8873                 else
8874                 {
8875                     newchar->pshotno = get_cached_model_index(value);
8876                 }
8877                 break;
8878             case CMD_MODEL_STAR:
8879                 value = GET_ARG(1);
8880                 if(stricmp(value, "none") == 0)
8881                 {
8882                     newchar->star = -1;
8883                 }
8884                 else
8885                 {
8886                     newchar->star = get_cached_model_index(value);
8887                 }
8888                 break;
8889             case CMD_MODEL_BOOMERANG:
8890                 newchar->boomerang_acc = GET_FLOAT_ARG(1);
8891                 newchar->boomerang_distx = GET_FLOAT_ARG(2);
8892             case CMD_MODEL_BOMB:
8893             case CMD_MODEL_PLAYBOMB:
8894                 value = GET_ARG(1);
8895                 if(stricmp(value, "none") == 0)
8896                 {
8897                     newchar->bomb = -1;
8898                 }
8899                 else
8900                 {
8901                     newchar->bomb = get_cached_model_index(value);
8902                 }
8903                 break;
8904             case CMD_MODEL_FLASH:	 // Now all characters can have their own flash - even projectiles (useful for blood)
8905                 value = GET_ARG(1);
8906                 if(stricmp(value, "none") == 0)
8907                 {
8908                     newchar->flash = -1;
8909                 }
8910                 else
8911                 {
8912                     newchar->flash = get_cached_model_index(value);
8913                 }
8914                 break;
8915             case CMD_MODEL_BFLASH:	// Flash that is spawned if an attack is blocked
8916                 value = GET_ARG(1);
8917                 if(stricmp(value, "none") == 0)
8918                 {
8919                     newchar->bflash = -1;
8920                 }
8921                 else
8922                 {
8923                     newchar->bflash = get_cached_model_index(value);
8924                 }
8925                 break;
8926             case CMD_MODEL_DUST:	// Spawned when hitting the ground to "kick up dust"
8927                 value = GET_ARG(1);
8928                 if(stricmp(value, "none") == 0)
8929                 {
8930                     newchar->dust.fall_land = -1;
8931                 }
8932                 else
8933                 {
8934                     newchar->dust.fall_land = get_cached_model_index(value);
8935                 }
8936                 value = GET_ARG(2);
8937                 if(stricmp(value, "none") == 0)
8938                 {
8939                     newchar->dust.jump_land = -1;
8940                 }
8941                 else
8942                 {
8943                     newchar->dust.jump_land = get_cached_model_index(value);
8944                 }
8945                 value = GET_ARG(3);
8946                 if(stricmp(value, "none") == 0)
8947                 {
8948                     newchar->dust.jump_start = -1;
8949                 }
8950                 else
8951                 {
8952                     newchar->dust.jump_start = get_cached_model_index(value);
8953                 }
8954                 break;
8955             case CMD_MODEL_BRANCH: // for endlevel item's level branch
8956                 value = GET_ARG(1);
8957                 if(!newchar->branch)
8958                 {
8959                     newchar->branch = malloc(MAX_NAME_LEN + 1);
8960                     newchar->branch[0] = 0;
8961                 }
8962                 strncpy(newchar->branch, value, MAX_NAME_LEN);
8963                 break;
8964             case CMD_MODEL_CANTGRAB:
8965             case CMD_MODEL_NOTGRAB:
8966                 tempInt = GET_INT_ARG(1);
8967                 if(tempInt == 2)
8968                 {
8969                     newchar->grabforce = -999999;
8970                 }
8971                 else
8972                 {
8973                     newchar->antigrab = 1;
8974                 }
8975                 break;
8976             case CMD_MODEL_ANTIGRAB: // a can grab b: a->antigrab - b->grabforce <=0
8977                 newchar->antigrab = GET_INT_ARG(1);
8978                 break;
8979             case CMD_MODEL_GRABFORCE:
8980                 newchar->grabforce = GET_INT_ARG(1);
8981                 break;
8982             case CMD_MODEL_GRABBACK:
8983                 newchar->grabback = GET_INT_ARG(1);
8984                 break;
8985             case CMD_MODEL_OFFSCREENKILL:
8986                 newchar->offscreenkill = GET_INT_ARG(1);
8987                 break;
8988             case CMD_MODEL_ONAF:
8989                 newchar->offscreen_noatk_factor = GET_FLOAT_ARG(1);
8990                 break;
8991             case CMD_MODEL_FALLDIE:
8992             case CMD_MODEL_DEATH:
8993                 newchar->falldie = GET_INT_ARG(1);
8994                 break;
8995             case CMD_MODEL_SPEED:
8996                 value = GET_ARG(1);
8997                 newchar->speed = atof(value);
8998                 newchar->speed /= 10;
8999                 if(newchar->speed < 0.5)
9000                 {
9001                     newchar->speed = 0.5;
9002                 }
9003                 if(newchar->speed > 30)
9004                 {
9005                     newchar->speed = 30;
9006                 }
9007                 break;
9008             case CMD_MODEL_SPEEDF:
9009                 value = GET_ARG(1);
9010                 newchar->speed = atof(value);
9011                 break;
9012             case CMD_MODEL_JUMPSPEED:
9013                 value = GET_ARG(1);
9014                 newchar->jumpspeed = atof(value);
9015                 newchar->jumpspeed /= 10;
9016                 break;
9017             case CMD_MODEL_JUMPSPEEDF:
9018                 value = GET_ARG(1);
9019                 newchar->jumpspeed = atof(value);
9020                 break;
9021             case CMD_MODEL_ANTIGRAVITY:
9022                 value = GET_ARG(1);
9023                 newchar->antigravity = atof(value);
9024                 newchar->antigravity /= 100;
9025                 break;
9026             case CMD_MODEL_STEALTH:
9027                 newchar->stealth.hide = GET_INT_ARG(1);
9028                 newchar->stealth.detect = GET_INT_ARG(2);
9029                 break;
9030             case CMD_MODEL_JUGGLEPOINTS:
9031                 value = GET_ARG(1);
9032                 newchar->jugglepoints.current = atoi(value);
9033                 newchar->jugglepoints.max = atoi(value);
9034                 break;
9035             case CMD_MODEL_RISEATTACKTYPE:
9036                 value = GET_ARG(1);
9037                 newchar->riseattacktype = atoi(value);
9038                 break;
9039             case CMD_MODEL_GUARDPOINTS:
9040                 value = GET_ARG(1);
9041                 newchar->guardpoints.current = atoi(value);
9042                 newchar->guardpoints.max = atoi(value);
9043                 break;
9044             case CMD_MODEL_DEFENSE:
9045 #define tempdef(x, y) \
9046 					x(stricmp(value, #y)==0)\
9047 					{\
9048 						newchar->defense[ATK_##y] = defense;\
9049 					}
9050             {
9051                 value = GET_ARG(1);
9052                 defense = default_defense;
9053                 if(newchar->subtype == SUBTYPE_BIKER)
9054                 {
9055                     defense.factor = 2.f;
9056                 }
9057 
9058                 if(arglist.count >= 2)
9059                 {
9060                     defense.factor = GET_FLOAT_ARG(2);
9061                 }
9062                 if(arglist.count >= 3)
9063                 {
9064                     defense.pain = GET_FLOAT_ARG(3);
9065                 }
9066                 if(arglist.count >= 4)
9067                 {
9068                     defense.knockdown = GET_FLOAT_ARG(4);
9069                 }
9070                 if(arglist.count >= 5)
9071                 {
9072                     defense.blockpower = GET_FLOAT_ARG(5);
9073                 }
9074                 if(arglist.count >= 6)
9075                 {
9076                     defense.blockthreshold = GET_FLOAT_ARG(6);
9077                 }
9078                 if(arglist.count >= 7)
9079                 {
9080                     defense.blockratio = GET_FLOAT_ARG(7);
9081                 }
9082                 if(arglist.count >= 8)
9083                 {
9084                     defense.blocktype = GET_FLOAT_ARG(8);
9085                 }
9086 
9087                 tempdef(if, NORMAL)
9088                     tempdef(else if, NORMAL2)
9089                         tempdef(else if, NORMAL3)
9090                             tempdef(else if, NORMAL4)
9091                                 tempdef(else if, NORMAL5)
9092                                     tempdef(else if, NORMAL6)
9093                                         tempdef(else if, NORMAL7)
9094                                             tempdef(else if, NORMAL8)
9095                                                 tempdef(else if, NORMAL9)
9096                                                     tempdef(else if, NORMAL10)
9097                                                         tempdef(else if, BLAST)
9098                                                             tempdef(else if, STEAL)
9099                                                                 tempdef(else if, BURN)
9100                                                                     tempdef(else if, SHOCK)
9101                                                                         tempdef(else if, FREEZE)
9102                                                                             tempdef(else if, ITEM)
9103                                                                                 tempdef(else if, LAND)
9104                                                                                     tempdef(else if, PIT)
9105                                                                                         tempdef(else if, LIFESPAN)
9106                                                                                             tempdef(else if, TIMEOVER)
9107                                                                                                 else if(starts_with(value, "normal"))
9108                                                                                                 {
9109                                                                                                     get_tail_number(tempInt, value, "normal");
9110                                                                                                     newchar->defense[tempInt + STA_ATKS - 1] = defense;
9111                                                                                                 }
9112                                                                                                 else if(stricmp(value, "ALL") == 0)
9113                                                                                                 {
9114                                                                                                     for(i = 0; i < max_attack_types; i++)
9115                                                                                                     {
9116                                                                                                         /*
9117                                                                                                         Skip the pit, lifespan, and time over attack types as these are for engine use. Nothing stops an author from defining defense settings for them individually.
9118                                                                                                         */
9119                                                                                                         if(i != ATK_PIT && i != ATK_TIMEOVER && i != ATK_LIFESPAN)
9120                                                                                                         {
9121                                                                                                             newchar->defense[i] = defense;
9122                                                                                                         }
9123                                                                                                     }
9124                                                                                                 }
9125             }
9126 #undef tempdef
9127             break;
9128             case CMD_MODEL_OFFENSE:
9129 #define tempoff(x, y, z) \
9130 					x(stricmp(value, #y)==0)\
9131 					{\
9132 					newchar->z[ATK_##y] = GET_FLOAT_ARG(2);\
9133 					/*newchar->z[ATK_##y] /= 100;*/\
9134 					}
9135             {
9136                 value = GET_ARG(1);
9137                 tempoff(if,         NORMAL,     offense_factors)
9138                     tempoff(else if,    NORMAL2,    offense_factors)
9139                         tempoff(else if,    NORMAL3,    offense_factors)
9140                             tempoff(else if,    NORMAL4,    offense_factors)
9141                                 tempoff(else if,    NORMAL5,    offense_factors)
9142                                     tempoff(else if,    NORMAL6,    offense_factors)
9143                                         tempoff(else if,    NORMAL7,    offense_factors)
9144                                             tempoff(else if,    NORMAL8,    offense_factors)
9145                                                 tempoff(else if,    NORMAL9,    offense_factors)
9146                                                     tempoff(else if,    NORMAL10,   offense_factors)
9147                                                         tempoff(else if,    BLAST,      offense_factors)
9148                                                             tempoff(else if,    STEAL,      offense_factors)
9149                                                                 tempoff(else if,    BURN,       offense_factors)
9150                                                                     tempoff(else if,    SHOCK,      offense_factors)
9151                                                                         tempoff(else if,    FREEZE,     offense_factors)
9152                                                                             tempoff(else if,    ITEM,		offense_factors)
9153                                                                                 tempoff(else if,    LAND,		offense_factors)
9154                                                                                     tempoff(else if,    PIT,		offense_factors)
9155                                                                                         tempoff(else if,    LIFESPAN,   offense_factors)
9156                                                                                             tempoff(else if,    TIMEOVER,   offense_factors)
9157                                                                                                 else if(starts_with(value, "normal"))
9158                                                                                                 {
9159                                                                                                     get_tail_number(tempInt, value, "normal");
9160                                                                                                     newchar->offense_factors[tempInt + STA_ATKS - 1] = GET_FLOAT_ARG(2);
9161                                                                                                 }
9162                                                                                                 else if(stricmp(value, "ALL") == 0)
9163                                                                                                 {
9164                                                                                                     tempFloat = GET_FLOAT_ARG(2);
9165                                                                                                     for(i = 0; i < max_attack_types; i++)
9166                                                                                                     {
9167                                                                                                         //offense hardly need those, just in case
9168                                                                                                         if(i != ATK_PIT && i != ATK_TIMEOVER && i != ATK_LIFESPAN)
9169                                                                                                         {
9170                                                                                                             newchar->offense_factors[i] = tempFloat;
9171                                                                                                         }
9172                                                                                                     }
9173                                                                                                 }
9174             }
9175 #undef tempoff
9176             break;
9177             case CMD_MODEL_HEIGHT:
9178                 newchar->size.y = GET_INT_ARG(1);
9179                 break;
9180             case CMD_MODEL_JUMPHEIGHT:
9181                 newchar->jumpheight = GET_FLOAT_ARG(1);
9182                 break;
9183             case CMD_MODEL_JUMPMOVE:
9184                 newchar->jumpmovex = GET_INT_ARG(1);
9185                 newchar->jumpmovez = GET_INT_ARG(2);
9186                 break;
9187             case CMD_MODEL_WALKOFFMOVE:
9188                 newchar->walkoffmovex = GET_INT_ARG(1);
9189                 newchar->walkoffmovez = GET_INT_ARG(2);
9190                 break;
9191             case CMD_MODEL_KNOCKDOWNCOUNT:
9192                 newchar->knockdowncount = GET_FLOAT_ARG(1);
9193                 break;
9194             case CMD_MODEL_GRABDISTANCE:
9195                 newchar->grabdistance = GET_FLOAT_ARG(1);                    // 30-12-2004 and store for character
9196                 break;
9197             case CMD_MODEL_GRABFLIP:
9198                 newchar->grabflip = GET_INT_ARG(1);
9199                 break;
9200             case CMD_MODEL_GRABFINISH:
9201                 newchar->grabfinish = GET_INT_ARG(1);
9202                 break;
9203             case CMD_MODEL_THROWDAMAGE:
9204                 newchar->throwdamage = GET_INT_ARG(1);
9205                 break;
9206             case CMD_MODEL_SHADOW:
9207                 newchar->shadow = GET_INT_ARG(1);
9208                 newchar->shadowbase = GET_INT_ARG(2);
9209                 break;
9210             case CMD_MODEL_GFXSHADOW:
9211                 newchar->gfxshadow = GET_INT_ARG(1);
9212                 newchar->shadowbase = GET_INT_ARG(2);
9213                 break;
9214             case CMD_MODEL_AIRONLY:	// Shadows display in air only?
9215                 newchar->aironly = GET_INT_ARG(1);
9216                 break;
9217             case CMD_MODEL_FMAP:	// Map that corresponds with the remap when a character is frozen
9218                 newchar->maps.frozen = GET_INT_ARG(1);
9219                 break;
9220             case CMD_MODEL_KOMAP:	// Remap when character is KO'd.
9221                 newchar->maps.ko = GET_INT_ARG(1);  //Remap.
9222                 newchar->maps.kotype = GET_INT_ARG(2);  //Type: 0 start of fall/death, 1 last frame.
9223                 break;
9224             case CMD_MODEL_HMAP:	// Maps range unavailable to player in select screen.
9225                 newchar->maps.hide_start = GET_INT_ARG(1); //First unavailable map.
9226                 newchar->maps.hide_end = GET_INT_ARG(2); //Last unavailable map.
9227                 break;
9228             case CMD_MODEL_SETLAYER:
9229                 newchar->setlayer = GET_INT_ARG(1);
9230                 break;
9231             case CMD_MODEL_TOFLIP:	  // Flag to determine if flashes images will be flipped or not
9232                 newchar->toflip = GET_INT_ARG(1);
9233                 break;
9234             case CMD_MODEL_NODIEBLINK:
9235                 // Added to determine if dying animation blinks or not
9236                 newchar->nodieblink = GET_INT_ARG(1);
9237                 break;
9238             case CMD_MODEL_NOATFLASH:	 // Flag to determine if an opponents attack spawns their flash or not
9239                 newchar->noatflash = GET_INT_ARG(1);
9240                 break;
9241             case CMD_MODEL_NOMOVE:
9242                 // If set, will be static (speed must be set to 0 or left blank)
9243                 newchar->nomove = GET_INT_ARG(1);
9244                 newchar->noflip = GET_INT_ARG(2);    // If set, static will not flip directions
9245                 if(newchar->nomove)
9246                 {
9247                     newchar->nodrop = 1;
9248                 }
9249                 break;
9250             case CMD_MODEL_NODROP:
9251                 newchar->nodrop = GET_INT_ARG(1);
9252                 break;
9253             case CMD_MODEL_THOLD:
9254                 // Threshold for enemies/players block
9255                 newchar->thold = GET_INT_ARG(1);
9256                 break;
9257             case CMD_MODEL_RUNNING:
9258                 // The speed at which the player runs
9259                 newchar->runspeed = GET_FLOAT_ARG(1);
9260                 newchar->runspeed /= 10;
9261                 newchar->runjumpheight = GET_FLOAT_ARG(2);    // The height at which a player jumps when running
9262                 newchar->runjumpdist = GET_FLOAT_ARG(3);    // The distance a player jumps when running
9263                 newchar->runupdown = GET_INT_ARG(4);
9264                 newchar->runhold = GET_INT_ARG(5);
9265                 break;
9266             case CMD_MODEL_RUNNING_CONTINUE:
9267                 newchar->runhold = GET_FLOAT_ARG(1);
9268                 break;
9269             case CMD_MODEL_RUNNING_JUMP_VELOCITY_X:
9270                 newchar->runjumpdist = GET_FLOAT_ARG(1);
9271                 break;
9272             case CMD_MODEL_RUNNING_JUMP_VELOCITY_Y:
9273                 newchar->runjumpheight = GET_FLOAT_ARG(1);
9274                 break;
9275             case CMD_MODEL_RUNNING_SPEED:
9276                 newchar->runspeed = GET_FLOAT_ARG(1);
9277                 break;
9278             case CMD_MODEL_RUNNING_Z_MOVE:
9279                 newchar->runupdown = GET_FLOAT_ARG(1);
9280                 break;
9281             case CMD_MODEL_BLOCKODDS:
9282                 // Odds that an attack will hit an enemy (1 : blockodds)
9283                 newchar->blockodds = GET_INT_ARG(1);
9284                 break;
9285             case CMD_MODEL_HOLDBLOCK:
9286                 newchar->holdblock = GET_INT_ARG(1);
9287                 break;
9288             case CMD_MODEL_BLOCKPAIN:
9289                 newchar->blockpain = GET_INT_ARG(1);
9290                 break;
9291             case CMD_MODEL_NOPASSIVEBLOCK:
9292                 newchar->nopassiveblock = GET_INT_ARG(1);
9293                 break;
9294             case CMD_MODEL_EDELAY:
9295                 newchar->edelay.mode        = GET_INT_ARG(1);
9296                 newchar->edelay.factor      = GET_FLOAT_ARG(2);
9297                 newchar->edelay.cap.min     = GET_INT_ARG(3);
9298                 newchar->edelay.cap.max     = GET_INT_ARG(4);
9299                 newchar->edelay.range.min   = GET_INT_ARG(5);
9300                 newchar->edelay.range.max   = GET_INT_ARG(6);
9301                 break;
9302             case CMD_MODEL_PAINGRAB:
9303                 newchar->paingrab = GET_INT_ARG(1);
9304                 break;
9305             case CMD_MODEL_THROW:
9306                 newchar->throwdist = GET_FLOAT_ARG(1);
9307                 newchar->throwheight = GET_FLOAT_ARG(2);
9308                 break;
9309             case CMD_MODEL_GRABWALK:
9310                 newchar->grabwalkspeed = GET_FLOAT_ARG(1);
9311                 newchar->grabwalkspeed /= 10;
9312                 if(newchar->grabwalkspeed < 0.5)
9313                 {
9314                     newchar->grabwalkspeed = 0.5;
9315                 }
9316                 break;
9317             case CMD_MODEL_GRABTURN:
9318                 newchar->grabturn = GET_INT_ARG(1);
9319                 break;
9320             case CMD_MODEL_THROWFRAMEWAIT:
9321                 newchar->throwframewait = GET_INT_ARG(1);
9322                 break;
9323             case CMD_MODEL_DIESOUND:
9324                 newchar->diesound = sound_load_sample(GET_ARG(1), packfile, 1);
9325                 break;
9326             case CMD_MODEL_ICON:
9327                 value = GET_ARG(1);
9328                 if(newchar->icon.def > -1)
9329                 {
9330                     shutdownmessage = "model has multiple icons defined";
9331                     goto lCleanup;
9332                 }
9333                 newchar->icon.def = loadsprite(value, 0, 0, pixelformat); //use same palette as the owner
9334                 newchar->icon.pain = newchar->icon.def;
9335                 newchar->icon.die = newchar->icon.def;
9336                 newchar->icon.get = newchar->icon.def;
9337                 newchar->icon.usemap = GET_INT_ARG(2); //be more friendly to some old mods which don't care about icon remap
9338                 break;
9339             case CMD_MODEL_ICONPAIN:
9340                 value = GET_ARG(1);
9341                 newchar->icon.pain = loadsprite(value, 0, 0, pixelformat);
9342                 break;
9343             case CMD_MODEL_ICONDIE:
9344                 value = GET_ARG(1);
9345                 newchar->icon.die = loadsprite(value, 0, 0, pixelformat);
9346                 break;
9347             case CMD_MODEL_ICONGET:
9348                 value = GET_ARG(1);
9349                 newchar->icon.get = loadsprite(value, 0, 0, pixelformat);
9350                 break;
9351             case CMD_MODEL_ICONW:
9352                 value = GET_ARG(1);
9353                 newchar->icon.weapon = loadsprite(value, 0, 0, pixelformat);
9354                 break;
9355             case CMD_MODEL_ICONMPHIGH:
9356                 value = GET_ARG(1);
9357                 newchar->icon.mphigh = loadsprite(value, 0, 0, pixelformat);
9358                 break;
9359             case CMD_MODEL_ICONMPHALF:
9360                 value = GET_ARG(1);
9361                 newchar->icon.mpmed = loadsprite(value, 0, 0, pixelformat);
9362                 break;
9363             case CMD_MODEL_ICONMPLOW:
9364                 value = GET_ARG(1);
9365                 newchar->icon.mplow = loadsprite(value, 0, 0, pixelformat);
9366                 break;
9367             case CMD_MODEL_PARROW:
9368                 // Image that is displayed when player 1 spawns invincible
9369                 value = GET_ARG(1);
9370                 newchar->parrow[0][0] = loadsprite(value, 0, 0, pixelformat);
9371                 newchar->parrow[0][1] = GET_INT_ARG(2);
9372                 newchar->parrow[0][2] = GET_INT_ARG(3);
9373                 break;
9374             case CMD_MODEL_PARROW2:
9375                 // Image that is displayed when player 2 spawns invincible
9376                 value = GET_ARG(1);
9377                 newchar->parrow[1][0] = loadsprite(value, 0, 0, pixelformat);
9378                 newchar->parrow[1][1] = GET_INT_ARG(2);
9379                 newchar->parrow[1][2] = GET_INT_ARG(3);
9380                 break;
9381             case CMD_MODEL_PARROW3:
9382                 value = GET_ARG(1);
9383                 newchar->parrow[2][0] = loadsprite(value, 0, 0, pixelformat);
9384                 newchar->parrow[2][1] = GET_INT_ARG(2);
9385                 newchar->parrow[2][2] = GET_INT_ARG(3);
9386                 break;
9387             case CMD_MODEL_PARROW4:
9388                 value = GET_ARG(1);
9389                 newchar->parrow[3][0] = loadsprite(value, 0, 0, pixelformat);
9390                 newchar->parrow[3][1] = GET_INT_ARG(2);
9391                 newchar->parrow[3][2] = GET_INT_ARG(3);
9392                 break;
9393             case CMD_MODEL_ATCHAIN:
9394                 newchar->chainlength = 0;
9395                 for(i = 0; i < MAX_ATCHAIN; i++)
9396                 {
9397                     newchar->atchain[i] = GET_INT_ARG(i + 1);
9398                     if(newchar->atchain[i] < 0)
9399                     {
9400                         newchar->atchain[i] = 0;
9401                     }
9402                     if(newchar->atchain[i] > max_attacks)
9403                     {
9404                         newchar->atchain[i] = max_attacks;
9405                     }
9406                     if(newchar->atchain[i])
9407                     {
9408                         newchar->chainlength = i + 1;
9409                     }
9410                 }
9411                 break;
9412             case CMD_MODEL_COMBOSTYLE:
9413                 newchar->combostyle = GET_INT_ARG(1);
9414                 break;
9415             case CMD_MODEL_CREDIT:
9416                 newchar->credit = GET_INT_ARG(1);
9417                 break;
9418             case CMD_MODEL_NOPAIN:
9419                 newchar->nopain = GET_INT_ARG(1);
9420                 break;
9421             case CMD_MODEL_ESCAPEHITS:
9422                 // How many times an enemy can be hit before retaliating
9423                 newchar->escapehits = GET_INT_ARG(1);
9424                 break;
9425             case CMD_MODEL_CHARGERATE:
9426                 // How much mp does this character gain while recharging?
9427                 newchar->chargerate = GET_INT_ARG(1);
9428                 break;
9429             case CMD_MODEL_MPRATE:
9430                 newchar->mprate = GET_INT_ARG(1);
9431                 break;
9432             case CMD_MODEL_MPSET:
9433                 // Mp bar wax/wane.
9434                 newchar->mp             = GET_INT_ARG(1); //Max MP.
9435                 newchar->mpstable       = GET_INT_ARG(2); //MP stable setting.
9436                 newchar->mpstableval    = GET_INT_ARG(3); //MP stable value (% Mp bar will try and maintain).
9437                 newchar->mprate         = GET_INT_ARG(4); //Rate MP value rises over time.
9438                 newchar->mpdroprate     = GET_INT_ARG(5); //Rate MP value drops over time.
9439                 newchar->chargerate     = GET_INT_ARG(6); //MP Chargerate.
9440                 break;
9441             case CMD_MODEL_SLEEPWAIT:
9442                 newchar->sleepwait = GET_INT_ARG(1);
9443                 break;
9444             case CMD_MODEL_GUARDRATE:
9445                 newchar->guardrate = GET_INT_ARG(1);
9446                 break;
9447             case CMD_MODEL_AGGRESSION:
9448                 newchar->aggression = GET_INT_ARG(1);
9449                 break;
9450             case CMD_MODEL_ATTACKTHROTTLE:
9451                 newchar->attackthrottle = GET_FLOAT_ARG(1);
9452                 if(arglist.count >= 2)
9453                 {
9454                     newchar->attackthrottletime = GET_FLOAT_ARG(2) * GAME_SPEED;
9455                 }
9456                 break;
9457             case CMD_MODEL_RISETIME:
9458                 newchar->risetime.rise = GET_INT_ARG(1);
9459                 newchar->risetime.riseattack = GET_INT_ARG(2);
9460                 break;
9461             case CMD_MODEL_FACING:
9462                 newchar->facing = GET_INT_ARG(1);
9463                 break;
9464             case CMD_MODEL_TURNDELAY:
9465                 newchar->turndelay = GET_INT_ARG(1);
9466                 break;
9467             case CMD_MODEL_LIFESPAN:
9468                 newchar->lifespan = GET_FLOAT_ARG(1) * GAME_SPEED;
9469                 break;
9470             case CMD_MODEL_SUMMONKILL:
9471                 newchar->summonkill = GET_INT_ARG(1);
9472                 break;
9473             case CMD_MODEL_LIFEPOSITION:
9474                 if((value = GET_ARG(1))[0])
9475                 {
9476                     newchar->hpx = atoi(value);
9477                 }
9478                 if((value = GET_ARG(2))[0])
9479                 {
9480                     newchar->hpy = atoi(value);
9481                 }
9482                 break;
9483             case CMD_MODEL_LIFEBARSTATUS:
9484                 _readbarstatus(buf + pos, &(newchar->hpbarstatus));
9485                 newchar->hpbarstatus.colourtable = &hpcolourtable;
9486                 break;
9487             case CMD_MODEL_ICONPOSITION:
9488                 if((value = GET_ARG(1))[0])
9489                 {
9490                     newchar->icon.position.x = atoi(value);
9491                 }
9492                 if((value = GET_ARG(2))[0])
9493                 {
9494                     newchar->icon.position.y = atoi(value);
9495                 }
9496                 break;
9497             case CMD_MODEL_NAMEPOSITION:
9498                 if((value = GET_ARG(1))[0])
9499                 {
9500                     newchar->namex = atoi(value);
9501                 }
9502                 if((value = GET_ARG(2))[0])
9503                 {
9504                     newchar->namey = atoi(value);
9505                 }
9506                 break;
9507             case CMD_MODEL_COM:
9508             {
9509                 // Section for custom freespecials starts here
9510                 int i, t;
9511                 int add_flag = 0;
9512                 alloc_specials(newchar);
9513                 newchar->special[newchar->specials_loaded].numkeys = 0;
9514                 for(i = 0, t = 1; i < MAX_SPECIAL_INPUTS - 3; i++, t++)
9515                 {
9516                     value = GET_ARG(t);
9517                     if(!value[0])
9518                     {
9519                         break;
9520                     }
9521                     if(stricmp(value, "u") == 0)
9522                     {
9523                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEUP;
9524                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEUP;
9525                         ++newchar->special[newchar->specials_loaded].numkeys;
9526                     }
9527                     else if(stricmp(value, "d") == 0)
9528                     {
9529                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEDOWN;
9530                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEDOWN;
9531                         ++newchar->special[newchar->specials_loaded].numkeys;
9532                     }
9533                     else if(stricmp(value, "f") == 0)
9534                     {
9535                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_FORWARD;
9536                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_FORWARD;
9537                         ++newchar->special[newchar->specials_loaded].numkeys;
9538                     }
9539                     else if(stricmp(value, "b") == 0)
9540                     {
9541                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_BACKWARD;
9542                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_BACKWARD;
9543                         ++newchar->special[newchar->specials_loaded].numkeys;
9544                     }
9545                     else if(stricmp(value, "a") == 0 || stricmp(value, "a1") == 0)
9546                     {
9547                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK;
9548                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK;
9549                         ++newchar->special[newchar->specials_loaded].numkeys;
9550                     }
9551                     else if(stricmp(value, "a2") == 0)
9552                     {
9553                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK2;
9554                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK2;
9555                         ++newchar->special[newchar->specials_loaded].numkeys;
9556                     }
9557                     else if(stricmp(value, "a3") == 0)
9558                     {
9559                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK3;
9560                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK3;
9561                         ++newchar->special[newchar->specials_loaded].numkeys;
9562                     }
9563                     else if(stricmp(value, "a4") == 0)
9564                     {
9565                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK4;
9566                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK4;
9567                         ++newchar->special[newchar->specials_loaded].numkeys;
9568                     }
9569                     else if(stricmp(value, "j") == 0)
9570                     {
9571                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_JUMP;
9572                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_JUMP;
9573                         ++newchar->special[newchar->specials_loaded].numkeys;
9574                     }
9575                     else if(stricmp(value, "s") == 0 || stricmp(value, "k") == 0)
9576                     {
9577                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_SPECIAL;
9578                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_SPECIAL;
9579                         ++newchar->special[newchar->specials_loaded].numkeys;
9580                     }
9581                     else if(starts_with_num(value, "freespecial"))
9582                     {
9583                         tempInt = atoi(value + 11);
9584                         if(tempInt < 1)
9585                         {
9586                             tempInt = 1;
9587                         }
9588                         newchar->special[newchar->specials_loaded].anim = animspecials[tempInt - 1];
9589                     }
9590                     else if(stricmp(value, "+") == 0 && i >= 1)
9591                     {
9592                         add_flag = 1;
9593                         i -= 2;
9594                         continue;
9595                     }
9596                     else if(stricmp(value, "->") == 0 && i > 0)
9597                     {
9598                         // just for better reading
9599                         --i;
9600                         continue;
9601                     }
9602                     else
9603                     {
9604                         shutdownmessage = "Invalid freespecial command";
9605                         goto lCleanup;
9606                     }
9607                     add_flag = 0;
9608                     //printf("insert:%s in %d, numkeys:%d for special n.:%d\n",value,i,newchar->special[newchar->specials_loaded].numkeys,newchar->specials_loaded);
9609                 }
9610                 newchar->special[newchar->specials_loaded].steps = i - 1; // max steps
9611                 newchar->specials_loaded++;
9612             }
9613             // End section for custom freespecials
9614             break;
9615             case CMD_MODEL_REMAP:
9616             {
9617                 // This command should not be used under 24bit mode, but for old mods, just give it a default palette
9618                 value = GET_ARG(1);
9619                 value2 = GET_ARG(2);
9620                 __realloc(mapflag, newchar->maps_loaded);
9621                 errorVal = load_colourmap(newchar, value, value2);
9622 
9623                 if(0 >= errorVal)
9624                 {
9625                     switch(errorVal)
9626                     {
9627                     case 0: // uhm wait, we just tested for !errorVal...
9628                         value2 = "Failed to create colourmap. Image Used Twice!";
9629                         break;
9630                     case -1: //should not happen now
9631                         value2 = "Failed to create colourmap. Color maps full error (color maps are unlimited by engine - check memory limits of console!";
9632                         break;
9633                     case -2:
9634                         value2 = "Failed to create colourmap. Failed to allocate memory!";
9635                         break;
9636                     case -3:
9637                         value2 = "Failed to create colourmap. Failed to load file 1";
9638                         break;
9639                     case -4:
9640                         value2 = "Failed to create colourmap. Failed to load file 2";
9641                         break;
9642                     }
9643                     printf("Warning: %s\n", value2);
9644                 }
9645                 else
9646                 {
9647                     if(pixelformat == PIXEL_x8 && newchar->palette == NULL)
9648                     {
9649                         newchar->palette = malloc(PAL_BYTES);
9650                         if(loadimagepalette(value, packfile, newchar->palette) == 0)
9651                         {
9652                             shutdownmessage = "Failed to load palette!";
9653                             goto lCleanup;
9654                         }
9655                     }
9656                     mapflag[newchar->maps_loaded - 1] = 1;
9657                 }
9658             }
9659             break;
9660             case CMD_MODEL_PALETTE:
9661                 // main palette for the entity under 24bit mode
9662                 if(pixelformat != PIXEL_x8)
9663                 {
9664                     printf("Warning: command '%s' is not available under 8bit mode\n", command);
9665                 }
9666                 else if(newchar->palette == NULL)
9667                 {
9668                     value = GET_ARG(1);
9669                     if(stricmp(value, "none") == 0)
9670                     {
9671                         if(pixelformat == PIXEL_x8)
9672                         {
9673                             nopalette = 1;
9674                         }
9675                     }
9676                     else
9677                     {
9678                         newchar->palette = malloc(PAL_BYTES);
9679                         if(loadimagepalette(value, packfile, newchar->palette) == 0)
9680                         {
9681                             shutdownmessage = "Failed to load palette!";
9682                             goto lCleanup;
9683                         }
9684                     }
9685                 }
9686                 break;
9687             case CMD_MODEL_ALTERNATEPAL:
9688                 // remap for the entity under 24bit mode, this method can replace remap command
9689                 if(pixelformat != PIXEL_x8)
9690                 {
9691                     printf("Warning: command '%s' is not available under 8bit mode\n", command);
9692                 }
9693                 else
9694                 {
9695                     __realloc(mapflag, newchar->maps_loaded);
9696                     __realloc(newchar->colourmap, newchar->maps_loaded);
9697                     value = GET_ARG(1);
9698                     newchar->colourmap[newchar->maps_loaded] = malloc(PAL_BYTES);
9699                     if(loadimagepalette(value, packfile, newchar->colourmap[newchar->maps_loaded]) == 0)
9700                     {
9701                         shutdownmessage = "Failed to load palette!";
9702                         goto lCleanup;
9703                     }
9704                     newchar->maps_loaded++;
9705                 }
9706                 break;
9707             case CMD_MODEL_GLOBALMAP:
9708                 // use global palette under 24bit mode, so some entity/panel/bg can still use palette feature, that saves some memory
9709                 if(pixelformat != PIXEL_x8)
9710                 {
9711                     printf("Warning: command '%s' is not available under 8bit mode\n", command);
9712                 }
9713                 else
9714                 {
9715                     newchar->globalmap = GET_INT_ARG(1);
9716                 }
9717                 break;
9718             case CMD_MODEL_ALPHA:
9719                 newchar->alpha = GET_INT_ARG(1);
9720                 break;
9721             case CMD_MODEL_REMOVE:
9722                 newchar->remove = GET_INT_ARG(1);
9723                 break;
9724             case CMD_MODEL_SCRIPT:
9725                 //load the update script
9726                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->update_script, "updateentityscript", filename, 1, 0);
9727                 break;
9728             case CMD_MODEL_THINKSCRIPT:
9729                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->think_script, "thinkscript", filename, 1, 0);
9730                 break;
9731             case CMD_MODEL_TAKEDAMAGESCRIPT:
9732                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->takedamage_script, "takedamagescript", filename, 1, 0);
9733                 break;
9734             case CMD_MODEL_ONFALLSCRIPT:
9735                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onfall_script, "onfallscript", filename, 1, 0);
9736                 break;
9737             case CMD_MODEL_ONPAINSCRIPT:
9738                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onpain_script, "onpainscript", filename, 1, 0);
9739                 break;
9740             case CMD_MODEL_INHOLESCRIPT:
9741                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->inhole_script, "inholescript", filename, 1, 0);
9742                 break;
9743             case CMD_MODEL_ONBLOCKSSCRIPT:
9744                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocks_script, "onblocksscript", filename, 1, 0);
9745                 break;
9746             case CMD_MODEL_ONBLOCKWSCRIPT:
9747                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockw_script, "onblockwscript", filename, 1, 0);
9748                 break;
9749             case CMD_MODEL_ONBLOCKPSCRIPT:
9750                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockp_script, "onblockpscript", filename, 1, 0);
9751                 break;
9752             case CMD_MODEL_ONBLOCKOSCRIPT:
9753                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocko_script, "onblockoscript", filename, 1, 0);
9754                 break;
9755             case CMD_MODEL_ONBLOCKZSCRIPT:
9756                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockz_script, "onblockzscript", filename, 1, 0);
9757                 break;
9758             case CMD_MODEL_ONBLOCKASCRIPT:
9759                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocka_script, "onblockascript", filename, 1, 0);
9760                 break;
9761             case CMD_MODEL_ONMOVEXSCRIPT:
9762                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovex_script, "onmovexscript", filename, 1, 0);
9763                 break;
9764             case CMD_MODEL_ONMOVEZSCRIPT:
9765                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovez_script, "onmovezscript", filename, 1, 0);
9766                 break;
9767             case CMD_MODEL_ONMOVEASCRIPT:
9768                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovea_script, "onmoveascript", filename, 1, 0);
9769                 break;
9770             case CMD_MODEL_ONDEATHSCRIPT:
9771                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondeath_script, "ondeathscript", filename, 1, 0);
9772                 break;
9773             case CMD_MODEL_ONKILLSCRIPT:
9774                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onkill_script, "onkillscript", filename, 1, 0);
9775                 break;
9776             case CMD_MODEL_DIDBLOCKSCRIPT:
9777                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->didblock_script, "didblockscript", filename, 1, 0);
9778                 break;
9779             case CMD_MODEL_ONDOATTACKSCRIPT:
9780                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondoattack_script, "ondoattackscript", filename, 1, 0);
9781                 break;
9782             case CMD_MODEL_DIDHITSCRIPT:
9783                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->didhit_script, "didhitscript", filename, 1, 0);
9784                 break;
9785             case CMD_MODEL_ONSPAWNSCRIPT:
9786                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onspawn_script, "onspawnscript", filename, 1, 0);
9787                 break;
9788             case CMD_MODEL_ONMODELCOPYSCRIPT:
9789                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmodelcopy_script, "onmodelcopyscript", filename, 1, 0);
9790                 break;
9791             case CMD_MODEL_ONDRAWSCRIPT:
9792                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondraw_script, "ondrawscript", filename, 1, 0);
9793                 break;
9794             case CMD_MODEL_ANIMATIONSCRIPT:
9795                 //pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->animation_script, "animationscript", filename, 0, 0);
9796                 pos += lcmScriptCopyBuffer(&arglist, buf + pos, &animscriptbuf);
9797                 //dont compile, until at end of this function
9798                 break;
9799             case CMD_MODEL_KEYSCRIPT:
9800                 pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->key_script, "entitykeyscript", filename, 1, 0);
9801                 break;
9802             case CMD_MODEL_ANIM:
9803             {
9804                 value = GET_ARG(1);
9805                 frameset = 0;
9806                 framecount = 0;
9807                 // Create new animation
9808                 newanim = alloc_anim();
9809                 if(!newanim)
9810                 {
9811                     shutdownmessage = "Not enough memory for animations!";
9812                     goto lCleanup;
9813                 }
9814                 newanim->model_index = newchar->index;
9815                 // Reset vars
9816                 curframe = 0;
9817                 memset(&bbox, 0, sizeof(bbox));
9818                 memset(&abox, 0, sizeof(abox));
9819                 memset(&offset, 0, sizeof(offset));
9820                 memset(shadow_coords, 0, sizeof(shadow_coords));
9821                 memset(shadow_xz, 0, sizeof(shadow_xz));
9822                 memset(platform, 0, sizeof(platform));
9823                 shadow_set                      = 0;
9824                 bbox_con                        = empty_body;
9825                 body_coords                     = empty_collision_coords;
9826                 attack                          = emptyattack;
9827                 attack_coords                   = empty_collision_coords;
9828                 recursive                       = empty_recursive;
9829                 attack.hitsound                 = SAMPLE_BEAT;
9830                 attack.hitflash                 = -1;
9831                 attack.blockflash               = -1;
9832                 attack.blocksound               = -1;
9833                 drawmethod                      = plainmethod;
9834                 idle                            = 0;
9835                 move.base                       = -1;
9836                 move.x                          = 0;
9837                 move.y                          = 0;
9838                 move.z                          = 0;
9839                 frameshadow                     = -1;
9840                 soundtoplay                     = -1;
9841 
9842                 if(!newanim->range.min.x)
9843                 {
9844                     newanim->range.min.x = -10;
9845                 }
9846                 newanim->range.max.x            = (int)newchar->jumpheight * 20;       //30-12-2004 default range affected by jump height
9847                 newanim->range.min.z            = (int) - newchar->grabdistance / 3;   //zmin
9848                 newanim->range.max.z            = (int)newchar->grabdistance / 3;      //zmax
9849                 newanim->range.min.y            = -1000;                               //amin
9850                 newanim->range.max.y            = 1000;                                //amax
9851                 newanim->range.min.base         = -1000;                            //Base min.
9852                 newanim->range.max.base         = 1000;                             //Base max.
9853                 newanim->jumpframe.velocity.y   = 0;  //Default disabled.
9854                 newanim->energycost             = NULL;
9855                 newanim->chargetime             = 2;			// Default for backwards compatibility
9856                 newanim->projectile.shootframe  = -1;
9857                 newanim->projectile.throwframe  = -1;
9858                 newanim->projectile.tossframe   = -1;			// this get 1 of weapons numshots shots in the animation that you want(normaly the last)by tails
9859                 newanim->jumpframe.frame        = -1;
9860                 newanim->flipframe              = -1;
9861                 newanim->attackone              = -1;
9862                 newanim->antigrav               = 0;
9863                 newanim->followup.animation     = 0;			// Default disabled
9864                 newanim->followup.condition     = FOLLOW_CONDITION_DISABLED;
9865                 newanim->unsummonframe          = -1;
9866                 newanim->landframe.frame        = -1;
9867                 newanim->dropframe              = NULL;
9868                 newanim->cancel                 = 0;  // OX. For cancelling anims into a freespecial. 0 by default , 3 when enabled. IMPORTANT!! Must stay as it is!
9869                 newanim->animhits               = 0; //OX counts hits on a per anim basis for cancels.
9870                 newanim->subentity              = newanim->projectile.bomb = newanim->projectile.knife = newanim->projectile.star = newanim->projectile.flash = -1;
9871                 newanim->quakeframe.framestart  = 0;
9872                 newanim->sync                   = -1;
9873 
9874                 if((ani_id = translate_ani_id(value, newchar, newanim, &attack)) < 0)
9875                 {
9876                     shutdownmessage = "Invalid animation name!";
9877                     goto lCleanup;
9878                 }
9879 
9880                 newchar->animation[ani_id] = newanim;
9881             }
9882             break;
9883             case CMD_MODEL_LOOP:
9884                 if(!newanim)
9885                 {
9886                     shutdownmessage = "Can't set loop: no animation specified!";
9887                     goto lCleanup;
9888                 }
9889                 newanim->loop.mode      = GET_INT_ARG(1); //0 = Off, 1 = on.
9890                 newanim->loop.frame.min = GET_INT_ARG(2); //Loop to frame.
9891                 newanim->loop.frame.max = GET_INT_ARG(3); //Loop end frame.
9892                 break;
9893             case CMD_MODEL_ANIMHEIGHT:
9894                 newanim->size.y = GET_INT_ARG(1);
9895                 break;
9896             case CMD_MODEL_SYNC:
9897                 //if you want to remove default sync setting for idle or walk, use none
9898                 newanim->sync = translate_ani_id(GET_ARG(1), NULL, NULL, NULL);
9899                 break;
9900             case CMD_MODEL_DELAY:
9901                 delay = GET_INT_ARG(1);
9902                 break;
9903             case CMD_MODEL_OFFSET:
9904                 offset.x = GET_INT_ARG(1);
9905                 offset.y = GET_INT_ARG(2);
9906                 break;
9907             case CMD_MODEL_SHADOWCOORDS:
9908                 shadow_xz[0] = GET_INT_ARG(1);
9909                 shadow_xz[1] = GET_INT_ARG(2);
9910                 shadow_set = 1;
9911                 break;
9912             case CMD_MODEL_ENERGYCOST:
9913             case CMD_MODEL_MPCOST:
9914                 if(!newanim->energycost)
9915                 {
9916                     newanim->energycost    = malloc(sizeof(*newanim->energycost));
9917                     memset(newanim->energycost, 0, sizeof(*newanim->energycost));
9918                 }
9919 
9920                 newanim->energycost->cost    = GET_INT_ARG(1);
9921                 newanim->energycost->mponly  = GET_INT_ARG(2);
9922                 newanim->energycost->disable = GET_INT_ARG(3);
9923 
9924                 break;
9925             case CMD_MODEL_MPONLY:
9926                 if(!newanim->energycost)
9927                 {
9928                     newanim->energycost    = malloc(sizeof(*newanim->energycost));
9929                     memset(newanim->energycost, 0, sizeof(*newanim->energycost));
9930                 }
9931 
9932                 newanim->energycost->mponly = GET_INT_ARG(1);
9933                 break;
9934             case CMD_MODEL_CHARGETIME:
9935                 newanim->chargetime = GET_FLOAT_ARG(1);
9936                 break;
9937             case CMD_MODEL_COLLISIONONE:
9938                 newanim->attackone = GET_INT_ARG(1);
9939                 break;
9940             case CMD_MODEL_COUNTERATTACK:
9941                 attack.counterattack = GET_INT_ARG(1);
9942                 break;
9943             case CMD_MODEL_THROWFRAME:
9944             case CMD_MODEL_PSHOTFRAME:
9945             case CMD_MODEL_PSHOTFRAMEW:
9946             case CMD_MODEL_PSHOTFRAMENO:
9947                 newanim->projectile.throwframe = GET_FRAME_ARG(1);
9948                 newanim->projectile.position.y = GET_INT_ARG(2);
9949                 if(!newanim->projectile.position.y)
9950                 {
9951                     newanim->projectile.position.y = 70;
9952                 }
9953                 else if(newanim->projectile.position.y == -1)
9954                 {
9955                     newanim->projectile.position.y = 0;
9956                 }
9957                 break;
9958             case CMD_MODEL_SHOOTFRAME:
9959                 newanim->projectile.shootframe = GET_FRAME_ARG(1);
9960                 newanim->projectile.position.y = GET_INT_ARG(2);
9961                 if(newanim->projectile.position.y == -1)
9962                 {
9963                     newanim->projectile.position.y = 0;
9964                 }
9965                 break;
9966             case CMD_MODEL_TOSSFRAME:
9967             case CMD_MODEL_PBOMBFRAME:
9968                 newanim->projectile.tossframe = GET_FRAME_ARG(1);
9969                 newanim->projectile.position.y = GET_INT_ARG(2);
9970                 if(newanim->projectile.position.y < 0)
9971                 {
9972                     newanim->projectile.position.y = -1;
9973                 }
9974                 break;
9975             case CMD_MODEL_CUSTKNIFE:
9976             case CMD_MODEL_CUSTPSHOT:
9977             case CMD_MODEL_CUSTPSHOTW:
9978                 newanim->projectile.knife = get_cached_model_index(GET_ARG(1));
9979                 break;
9980             case CMD_MODEL_CUSTPSHOTNO:
9981                 newanim->projectile.flash = get_cached_model_index(GET_ARG(1));
9982                 break;
9983             case CMD_MODEL_CUSTBOMB:
9984             case CMD_MODEL_CUSTPBOMB:
9985                 newanim->projectile.bomb = get_cached_model_index(GET_ARG(1));
9986                 break;
9987             case CMD_MODEL_CUSTSTAR:
9988                 newanim->projectile.star = get_cached_model_index(GET_ARG(1));
9989                 break;
9990 
9991                 // UT: merge dive and jumpframe, because they can't be used at the same time
9992             case CMD_MODEL_DIVE:	//antigrav kicks
9993                 newanim->antigrav = 1;
9994                 newanim->jumpframe.frame = 0;
9995                 newanim->jumpframe.velocity.x = GET_FLOAT_ARG(1);
9996                 newanim->jumpframe.velocity.y = -GET_FLOAT_ARG(2);
9997                 newanim->jumpframe.ent = -1;
9998                 break;
9999             case CMD_MODEL_DIVE1:
10000                 newanim->antigrav = 1;
10001                 newanim->jumpframe.frame = 0;
10002                 newanim->jumpframe.velocity.x = GET_FLOAT_ARG(1);
10003                 newanim->jumpframe.ent = -1;
10004                 break;
10005             case CMD_MODEL_DIVE2:
10006                 newanim->antigrav = 1;
10007                 newanim->jumpframe.frame = 0;
10008                 newanim->jumpframe.velocity.y = -GET_FLOAT_ARG(1);
10009                 newanim->jumpframe.ent = -1;
10010                 break;
10011             case CMD_MODEL_JUMPFRAME:
10012             {
10013                 newanim->jumpframe.frame    = GET_FRAME_ARG(1);   //Frame.
10014                 newanim->jumpframe.velocity.y    = GET_FLOAT_ARG(2); //Vertical velocity.
10015                 value = GET_ARG(3);
10016                 if(value[0])
10017                 {
10018                     newanim->jumpframe.velocity.x = GET_FLOAT_ARG(3);
10019                     newanim->jumpframe.velocity.z = GET_FLOAT_ARG(4);
10020                 }
10021                 else // k, only for backward compatibility :((((((((((((((((
10022                 {
10023                     if(newanim->jumpframe.velocity.y <= 0)
10024                     {
10025                         if(newchar->type == TYPE_PLAYER)
10026                         {
10027                             newanim->jumpframe.velocity.y = newchar->jumpheight / 2;
10028                             newanim->jumpframe.velocity.z = 0;
10029                             newanim->jumpframe.velocity.x = 2;
10030                         }
10031                         else
10032                         {
10033                             newanim->jumpframe.velocity.y = newchar->jumpheight;
10034                             newanim->jumpframe.velocity.z = newanim->jumpframe.velocity.x = 0;
10035                         }
10036                     }
10037                     else
10038                     {
10039                         if(newchar->type != TYPE_ENEMY && newchar->type != TYPE_NPC)
10040                         {
10041                             newanim->jumpframe.velocity.z = newanim->jumpframe.velocity.x = 0;
10042                         }
10043                         else
10044                         {
10045                             newanim->jumpframe.velocity.z = 0;
10046                             newanim->jumpframe.velocity.x = (float)1.3;
10047                         }
10048                     }
10049                 }
10050 
10051                 value = GET_ARG(5);
10052                 if(value[0])
10053                 {
10054                     newanim->jumpframe.ent = get_cached_model_index(value);
10055                 }
10056                 else
10057                 {
10058                     newanim->jumpframe.ent = -1;
10059                 }
10060 
10061             }
10062             break;
10063             case CMD_MODEL_BOUNCEFACTOR:
10064                 newanim->bounce = GET_FLOAT_ARG(1);
10065                 break;
10066             case CMD_MODEL_LANDFRAME:
10067                 newanim->landframe.frame = GET_FRAME_ARG(1);
10068                 value = GET_ARG(2);
10069                 if(value[0])
10070                 {
10071                     newanim->landframe.ent = get_cached_model_index(value);
10072                 }
10073                 else
10074                 {
10075                     newanim->landframe.ent = -1;
10076                 }
10077                 break;
10078             case CMD_MODEL_DROPFRAME:
10079                 newanim->dropframe    = malloc(sizeof(*newanim->dropframe));
10080                 memset(newanim->dropframe, 0, sizeof(*newanim->dropframe));
10081 
10082                 newanim->dropframe->frame = GET_FRAME_ARG(1);
10083                 break;
10084             case CMD_MODEL_CANCEL:
10085             {
10086                 int i, t;
10087                 int add_flag = 0;
10088                 alloc_specials(newchar);
10089                 newanim->cancel = 3;
10090                 newchar->special[newchar->specials_loaded].numkeys = 0;
10091                 for(i = 0, t = 4; i < MAX_SPECIAL_INPUTS - 6; i++, t++)
10092                 {
10093                     value = GET_ARG(t);
10094                     if(!value[0])
10095                     {
10096                         break;
10097                     }
10098                     if(stricmp(value, "u") == 0)
10099                     {
10100                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEUP;
10101                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEUP;
10102                         ++newchar->special[newchar->specials_loaded].numkeys;
10103                     }
10104                     else if(stricmp(value, "d") == 0)
10105                     {
10106                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_MOVEDOWN;
10107                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_MOVEDOWN;
10108                         ++newchar->special[newchar->specials_loaded].numkeys;
10109                     }
10110                     else if(stricmp(value, "f") == 0)
10111                     {
10112                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_FORWARD;
10113                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_FORWARD;
10114                         ++newchar->special[newchar->specials_loaded].numkeys;
10115                     }
10116                     else if(stricmp(value, "b") == 0)
10117                     {
10118                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_BACKWARD;
10119                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_BACKWARD;
10120                         ++newchar->special[newchar->specials_loaded].numkeys;
10121                     }
10122                     else if(stricmp(value, "a") == 0 || stricmp(value, "a1") == 0)
10123                     {
10124                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK;
10125                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK;
10126                         ++newchar->special[newchar->specials_loaded].numkeys;
10127                     }
10128                     else if(stricmp(value, "a2") == 0)
10129                     {
10130                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK2;
10131                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK2;
10132                         ++newchar->special[newchar->specials_loaded].numkeys;
10133                     }
10134                     else if(stricmp(value, "a3") == 0)
10135                     {
10136                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK3;
10137                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK3;
10138                         ++newchar->special[newchar->specials_loaded].numkeys;
10139                     }
10140                     else if(stricmp(value, "a4") == 0)
10141                     {
10142                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_ATTACK4;
10143                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_ATTACK4;
10144                         ++newchar->special[newchar->specials_loaded].numkeys;
10145                     }
10146                     else if(stricmp(value, "j") == 0)
10147                     {
10148                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_JUMP;
10149                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_JUMP;
10150                         ++newchar->special[newchar->specials_loaded].numkeys;
10151                     }
10152                     else if(stricmp(value, "s") == 0 || stricmp(value, "k") == 0)
10153                     {
10154                         if (!add_flag) newchar->special[newchar->specials_loaded].input[i] = FLAG_SPECIAL;
10155                         else newchar->special[newchar->specials_loaded].input[i] |= FLAG_SPECIAL;
10156                         ++newchar->special[newchar->specials_loaded].numkeys;
10157                     }
10158                     else if(starts_with_num(value, "freespecial"))
10159                     {
10160                         get_tail_number(tempInt, value, "freespecial");
10161                         newchar->special[newchar->specials_loaded].anim = animspecials[tempInt - 1];
10162                         newchar->special[newchar->specials_loaded].frame.min = GET_INT_ARG(1); // stores start frame
10163                         newchar->special[newchar->specials_loaded].frame.max = GET_INT_ARG(2); // stores end frame
10164                         newchar->special[newchar->specials_loaded].cancel = ani_id;                    // stores current anim
10165                         newchar->special[newchar->specials_loaded].hits = GET_INT_ARG(3);// stores hits
10166                     }
10167                     else if(stricmp(value, "+") == 0 && i > 1)
10168                     {
10169                         add_flag = 1;
10170                         i -= 2;
10171                         continue;
10172                     }
10173                     else if(stricmp(value, "->") == 0 && i > 0)
10174                     {
10175                         // just for better reading
10176                         --i;
10177                         continue;
10178                     }
10179                     else
10180                     {
10181                         shutdownmessage = "Invalid cancel command!";
10182                         goto lCleanup;
10183                     }
10184                     add_flag = 0;
10185                 }
10186                 newchar->special[newchar->specials_loaded].steps = i - 1; // max steps
10187                 newchar->specials_loaded++;
10188             }
10189             break;
10190             case CMD_MODEL_SOUND:
10191                 soundtoplay = sound_load_sample(GET_ARG(1), packfile, 1);
10192                 break;
10193             case CMD_MODEL_HITFX:
10194                 if(stricmp(GET_ARG(1), "none") == 0)
10195                 {
10196                     attack.hitsound = -1;
10197                 }
10198                 else
10199                 {
10200                     attack.hitsound = sound_load_sample(GET_ARG(1), packfile, 1);
10201                 }
10202                 break;
10203             case CMD_MODEL_HITFLASH:
10204                 value = GET_ARG(1);
10205                 if(stricmp(value, "none") == 0)
10206                 {
10207                     attack.hitflash = -1;
10208                 }
10209                 else
10210                 {
10211                     attack.hitflash = get_cached_model_index(value);
10212                 }
10213                 break;
10214             case CMD_MODEL_BLOCKFLASH:
10215                 value = GET_ARG(1);
10216                 if(stricmp(value, "none") == 0)
10217                 {
10218                     attack.blockflash = -1;
10219                 }
10220                 else
10221                 {
10222                     attack.blockflash = get_cached_model_index(value);
10223                 }
10224                 break;
10225             case CMD_MODEL_BLOCKFX:
10226                 attack.blocksound = sound_load_sample(GET_ARG(1), packfile, 1);
10227                 break;
10228             case CMD_MODEL_FASTATTACK:
10229                 if(GET_INT_ARG(1))
10230                 {
10231                     attack.pain_time = GAME_SPEED / 20;
10232                 }
10233                 break;
10234             case CMD_MODEL_IGNOREATTACKID:
10235                 if(GET_INT_ARG(1))
10236                 {
10237                     attack.ignore_attack_id = 1;
10238                 }
10239                 break;
10240             case CMD_MODEL_BBOX:
10241                 bbox.x = GET_INT_ARG(1);
10242                 bbox.y = GET_INT_ARG(2);
10243                 bbox.width = GET_INT_ARG(3);
10244                 bbox.height = GET_INT_ARG(4);
10245                 bbox.z1 = GET_INT_ARG(5);
10246                 bbox.z2 = GET_INT_ARG(6);
10247                 break;
10248             case CMD_MODEL_BBOX_INDEX:
10249                 // Nothing yet - for future support of multiple boxes.
10250                 break;
10251             case CMD_MODEL_BBOX_POSITION_X:
10252                 bbox.x = GET_INT_ARG(1);
10253                 break;
10254             case CMD_MODEL_BBOX_POSITION_Y:
10255                 bbox.y = GET_INT_ARG(1);
10256                 break;
10257             case CMD_MODEL_BBOX_SIZE_X:
10258                 bbox.width = GET_INT_ARG(1);
10259                 break;
10260             case CMD_MODEL_BBOX_SIZE_Y:
10261                 bbox.height = GET_INT_ARG(1);
10262                 break;
10263             case CMD_MODEL_BBOX_SIZE_Z_1:
10264                 bbox.z1 = GET_INT_ARG(1);
10265                 break;
10266             case CMD_MODEL_BBOX_SIZE_Z_2:
10267                 bbox.z2 = GET_INT_ARG(1);
10268                 break;
10269             case CMD_MODEL_BBOXZ:
10270                 bbox.z1 = GET_INT_ARG(1);
10271                 bbox.z2 = GET_INT_ARG(2);
10272                 break;
10273             case CMD_MODEL_PLATFORM:
10274                 newchar->hasPlatforms = 1;
10275                 //for(i=0;(GET_ARG(i+1)[0]; i++);
10276                 for(i = 0; i < arglist.count && arglist.args[i] && arglist.args[i][0]; i++);
10277                 if(i < 8)
10278                 {
10279                     for(i = 0; i < 6; i++)
10280                     {
10281                         platform[i + 2] = GET_FLOAT_ARG(i + 1);
10282                     }
10283                     platform[0] = 99999;
10284                 }
10285                 else for(i = 0; i < 8; i++)
10286                     {
10287                         platform[i] = GET_FLOAT_ARG(i + 1);
10288                     }
10289                 break;
10290             case CMD_MODEL_DRAWMETHOD:
10291                 value = GET_ARG(1);
10292                 if(isNumeric(value))
10293                 {
10294                     // special effects
10295                     drawmethod.scalex = GET_INT_ARG(1);
10296                     drawmethod.scaley = GET_INT_ARG(2);
10297                     drawmethod.flipx = GET_INT_ARG(3);
10298                     drawmethod.flipy = GET_INT_ARG(4);
10299                     drawmethod.shiftx = GET_INT_ARG(5);
10300                     drawmethod.alpha = GET_INT_ARG(6);
10301                     drawmethod.remap = GET_INT_ARG(7);
10302                     drawmethod.fillcolor = parsecolor(GET_ARG(8));
10303                     drawmethod.rotate = GET_INT_ARG(9);
10304                     drawmethod.fliprotate = GET_INT_ARG(10);
10305                 }
10306                 else if (0 == stricmp(value, "scale"))
10307                 {
10308                     drawmethod.scalex = GET_FLOAT_ARG(2) * 256;
10309                     drawmethod.scaley = arglist.count > 3 ? GET_FLOAT_ARG(3) * 256 : drawmethod.scalex;
10310                 }
10311                 else if (0 == stricmp(value, "scalex"))
10312                 {
10313                     drawmethod.scalex = GET_FLOAT_ARG(2) * 256;
10314                 }
10315                 else if (0 == stricmp(value, "scaley"))
10316                 {
10317                     drawmethod.scaley = GET_FLOAT_ARG(2) * 256;
10318                 }
10319                 else if (0 == stricmp(value, "xrepeat"))
10320                 {
10321                     drawmethod.xrepeat = GET_INT_ARG(2);
10322                 }
10323                 else if (0 == stricmp(value, "yrepeat"))
10324                 {
10325                     drawmethod.yrepeat = GET_INT_ARG(2);
10326                 }
10327                 else if (0 == stricmp(value, "xspan"))
10328                 {
10329                     drawmethod.xspan = GET_INT_ARG(2);
10330                 }
10331                 else if (0 == stricmp(value, "yspan"))
10332                 {
10333                     drawmethod.yspan = GET_INT_ARG(2);
10334                 }
10335                 else if (0 == stricmp(value, "flipx"))
10336                 {
10337                     drawmethod.flipx = GET_INT_ARG(2);
10338                 }
10339                 else if (0 == stricmp(value, "flipy"))
10340                 {
10341                     drawmethod.flipy = GET_INT_ARG(2);
10342                 }
10343                 else if (0 == stricmp(value, "shiftx"))
10344                 {
10345                     drawmethod.shiftx = GET_FLOAT_ARG(2) * 256;
10346                 }
10347                 else if (0 == stricmp(value, "rotate"))
10348                 {
10349                     drawmethod.rotate = GET_INT_ARG(2);
10350                 }
10351                 else if (0 == stricmp(value, "fliprotate"))
10352                 {
10353                     drawmethod.fliprotate = GET_INT_ARG(2);
10354                 }
10355                 else if (0 == stricmp(value, "fillcolor"))
10356                 {
10357                     drawmethod.fliprotate = parsecolor(GET_ARG(2));
10358                 }
10359                 else if (0 == stricmp(value, "remap"))
10360                 {
10361                     drawmethod.remap = GET_INT_ARG(2);
10362                 }
10363                 else if (0 == stricmp(value, "channel"))
10364                 {
10365                     drawmethod.channelr = GET_FLOAT_ARG(2) * 255;
10366                     drawmethod.channelg = arglist.count > 3 ? GET_FLOAT_ARG(3) * 255 : drawmethod.channelr;
10367                     drawmethod.channelb = arglist.count > 4 ? GET_FLOAT_ARG(4) * 255 : drawmethod.channelr;
10368                 }
10369                 else if (0 == stricmp(value, "channelr"))
10370                 {
10371                     drawmethod.channelr = GET_FLOAT_ARG(2) * 255;
10372                 }
10373                 else if (0 == stricmp(value, "channelg"))
10374                 {
10375                     drawmethod.channelg = GET_FLOAT_ARG(2) * 255;
10376                 }
10377                 else if (0 == stricmp(value, "channelb"))
10378                 {
10379                     drawmethod.channelb = GET_FLOAT_ARG(2) * 255;
10380                 }
10381                 else if (0 == stricmp(value, "tintmode"))
10382                 {
10383                     drawmethod.tintmode = GET_INT_ARG(2);
10384                 }
10385                 else if (0 == stricmp(value, "tintcolor"))
10386                 {
10387                     drawmethod.tintcolor = parsecolor(GET_ARG(2));
10388                 }
10389                 else if (0 == stricmp(value, "alpha"))
10390                 {
10391                     drawmethod.alpha = GET_INT_ARG(2);
10392                 }
10393                 else if (0 == stricmp(value, "clip"))
10394                 {
10395                     drawmethod.clipx = GET_INT_ARG(2);
10396                     drawmethod.clipy = GET_INT_ARG(3);
10397                     drawmethod.clipw = GET_INT_ARG(4);
10398                     drawmethod.cliph = GET_INT_ARG(5);
10399                 }
10400                 if(drawmethod.scalex < 0)
10401                 {
10402                     drawmethod.scalex = -drawmethod.scalex;
10403                     drawmethod.flipx = !drawmethod.flipx;
10404                 }
10405                 if(drawmethod.scaley < 0)
10406                 {
10407                     drawmethod.scaley = -drawmethod.scaley;
10408                     drawmethod.flipy = !drawmethod.flipy;
10409                 }
10410                 if(drawmethod.rotate)
10411                 {
10412                     drawmethod.rotate = ((int)drawmethod.rotate % 360 + 360) % 360;
10413                 }
10414                 if(!blendfx_is_set)
10415                 {
10416                     if(drawmethod.alpha > 0 && drawmethod.alpha <= MAX_BLENDINGS)
10417                     {
10418                         blendfx[drawmethod.alpha - 1] = 1;
10419                     }
10420                 }
10421                 drawmethod.flag = 1;
10422                 break;
10423             case CMD_MODEL_NODRAWMETHOD:
10424                 //disable special effects
10425                 drawmethod.flag = 0;
10426                 break;
10427 
10428             // 2016-10-11
10429             // Caskey, Damon
10430             // Broken down attack commands.
10431             case CMD_MODEL_COLLISION_BLOCK_COST:
10432                 attack.guardcost = GET_INT_ARG(1);
10433                 break;
10434             case CMD_MODEL_COLLISION_BLOCK_PENETRATE:
10435                 attack.no_block = GET_INT_ARG(1);
10436                 break;
10437             case CMD_MODEL_COLLISION_COUNTER:
10438                 attack.counterattack = GET_INT_ARG(1);
10439                 break;
10440             case CMD_MODEL_COLLISION_DAMAGE_FORCE:
10441                 attack.attack_force = GET_INT_ARG(1);
10442                 break;
10443             case CMD_MODEL_COLLISION_DAMAGE_LAND_FORCE:
10444                 attack.damage_on_landing = GET_INT_ARG(1);
10445                 break;
10446             case CMD_MODEL_COLLISION_DAMAGE_LAND_MODE:
10447                 attack.blast = GET_INT_ARG(1);
10448                 break;
10449             case CMD_MODEL_COLLISION_DAMAGE_LETHAL_DISABLE:
10450                 attack.no_kill = GET_INT_ARG(1);
10451                 break;
10452             case CMD_MODEL_COLLISION_DAMAGE_STEAL:
10453                 attack.steal = GET_INT_ARG(1);
10454                 break;
10455             case CMD_MODEL_COLLISION_DAMAGE_TYPE:
10456                 attack.attack_type = GET_INT_ARG(1);
10457                 break;
10458             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_FORCE:
10459                 recursive.force = GET_INT_ARG(1);
10460                 break;
10461             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_INDEX:
10462                 recursive.index = GET_INT_ARG(1);
10463                 break;
10464             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_MODE:
10465                 recursive.mode = GET_INT_ARG(1);
10466                 break;
10467             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TIME_RATE:
10468                 recursive.rate = GET_INT_ARG(1);
10469                 break;
10470             case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TIME_EXPIRE:
10471                 recursive.time = GET_INT_ARG(1);
10472                 break;
10473             case CMD_MODEL_COLLISION_REACTION_FALL_FORCE:
10474                 attack.attack_drop = GET_INT_ARG(1);
10475                 break;
10476             case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_X:
10477                 attack.dropv.x = GET_FLOAT_ARG(1);
10478                 break;
10479             case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_Y:
10480                 attack.dropv.y = GET_FLOAT_ARG(1);
10481                 break;
10482             case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_Z:
10483                 attack.dropv.z = GET_FLOAT_ARG(1);
10484                 break;
10485             case CMD_MODEL_COLLISION_EFFECT_BLOCK_FLASH:
10486 
10487                 value = GET_ARG(1);
10488 
10489                 if(stricmp(value, "none") == 0 || value == 0)
10490                 {
10491                     newchar->bflash = -1;
10492                 }
10493                 else
10494                 {
10495                     newchar->bflash = get_cached_model_index(value);
10496                 }
10497                 break;
10498 
10499             case CMD_MODEL_COLLISION_EFFECT_BLOCK_SOUND:
10500 
10501                 value = GET_ARG(1);
10502 
10503                 if(stricmp(value, "none") == 0)
10504                 {
10505                     attack.blocksound = -1;
10506                 }
10507                 else
10508                 {
10509                     attack.blocksound = sound_load_sample(value, packfile, 1);
10510                 }
10511                 break;
10512 
10513             case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH:
10514 
10515                 value = GET_ARG(1);
10516 
10517                 if(stricmp(value, "none") == 0 || value == 0)
10518                 {
10519                     newchar->flash = -1;
10520                 }
10521                 else
10522                 {
10523                     newchar->flash = get_cached_model_index(value);
10524                 }
10525                 break;
10526 
10527             case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH_DISABLE:
10528                 attack.no_flash = GET_INT_ARG(1);
10529                 break;
10530 
10531             case CMD_MODEL_COLLISION_EFFECT_HIT_SOUND:
10532 
10533                 value = GET_ARG(1);
10534 
10535                 if(stricmp(value, "none") == 0)
10536                 {
10537                     attack.hitsound = -1;
10538                 }
10539                 else
10540                 {
10541                     attack.hitsound = sound_load_sample(value, packfile, 1);
10542                 }
10543                 break;
10544             case CMD_MODEL_COLLISION_GROUND:
10545                 attack.otg = GET_INT_ARG(1);
10546                 break;
10547             case CMD_MODEL_COLLISION_MAP_INDEX:
10548                 attack.forcemap = GET_INT_ARG(1);
10549                 break;
10550             case CMD_MODEL_COLLISION_MAP_TIME:
10551                 attack.maptime = GET_INT_ARG(1);
10552                 break;
10553             case CMD_MODEL_COLLISION_POSITION_X:
10554                 abox.x = GET_INT_ARG(1);
10555                 break;
10556             case CMD_MODEL_COLLISION_POSITION_Y:
10557                 abox.y = GET_INT_ARG(1);
10558                 break;
10559             case CMD_MODEL_COLLISION_REACTION_FREEZE_MODE:
10560                 attack.freeze = GET_INT_ARG(1);
10561                 break;
10562             case CMD_MODEL_COLLISION_REACTION_FREEZE_TIME:
10563                 attack.freezetime = GET_INT_ARG(1);
10564                 break;
10565             case CMD_MODEL_COLLISION_REACTION_INVINCIBLE_TIME:
10566                 attack.pain_time = GET_INT_ARG(1);
10567                 break;
10568             case CMD_MODEL_COLLISION_REACTION_REPOSITION_DISTANCE:
10569                 attack.grab_distance = GET_INT_ARG(1);
10570                 break;
10571             case CMD_MODEL_COLLISION_REACTION_REPOSITION_MODE:
10572                 attack.grab = GET_INT_ARG(1);
10573                 break;
10574             case CMD_MODEL_COLLISION_REACTION_PAIN_SKIP:
10575                 attack.no_pain = GET_INT_ARG(1);
10576                 break;
10577             case CMD_MODEL_COLLISION_REACTION_PAUSE_TIME:
10578                 attack.pause_add = GET_INT_ARG(1);
10579                 break;
10580             case CMD_MODEL_COLLISION_SEAL_COST:
10581                 attack.seal = GET_INT_ARG(1);
10582                 break;
10583             case CMD_MODEL_COLLISION_SEAL_TIME:
10584                 attack.sealtime = GET_INT_ARG(1);
10585                 break;
10586             case CMD_MODEL_COLLISION_SIZE_X:
10587                 abox.width = GET_INT_ARG(1);
10588                 break;
10589             case CMD_MODEL_COLLISION_SIZE_Y:
10590                 abox.height = GET_INT_ARG(1);
10591                 break;
10592             case CMD_MODEL_COLLISION_SIZE_Z_1:
10593                 attack_coords.z1 = GET_INT_ARG(1);
10594                 break;
10595             case CMD_MODEL_COLLISION_SIZE_Z_2:
10596                 attack_coords.z2 = GET_INT_ARG(1);
10597                 break;
10598             case CMD_MODEL_COLLISION_STAYDOWN_RISE:
10599                 attack.staydown.rise = GET_INT_ARG(1);
10600                 break;
10601             case CMD_MODEL_COLLISION_STAYDOWN_RISEATTACK:
10602                 attack.staydown.riseattack = GET_INT_ARG(1);
10603                 break;
10604             case CMD_MODEL_COLLISION_TAG:
10605                 attack.tag = GET_INT_ARG(1);
10606                 break;
10607             case CMD_MODEL_COLLISION:
10608             case CMD_MODEL_COLLISION1:
10609             case CMD_MODEL_COLLISION2:
10610             case CMD_MODEL_COLLISION3:
10611             case CMD_MODEL_COLLISION4:
10612             case CMD_MODEL_COLLISION5:
10613             case CMD_MODEL_COLLISION6:
10614             case CMD_MODEL_COLLISION7:
10615             case CMD_MODEL_COLLISION8:
10616             case CMD_MODEL_COLLISION9:
10617             case CMD_MODEL_COLLISION10:
10618             case CMD_MODEL_SHOCK:
10619             case CMD_MODEL_BURN:
10620             case CMD_MODEL_STEAL:
10621             case CMD_MODEL_FREEZE:
10622             case CMD_MODEL_ITEMBOX:
10623             case CMD_MODEL_COLLISION_ETC:
10624                 abox.x = GET_INT_ARG(1);
10625                 abox.y = GET_INT_ARG(2);
10626                 abox.width = GET_INT_ARG(3);
10627                 abox.height = GET_INT_ARG(4);
10628                 attack.dropv.y = default_model_dropv.y;
10629                 attack.dropv.x = default_model_dropv.x;
10630                 attack.dropv.z = default_model_dropv.z;
10631                 attack.attack_force = GET_INT_ARG(5);
10632 
10633                 attack.attack_drop = GET_INT_ARG(6);
10634 
10635                 attack.no_block = GET_INT_ARG(7);
10636                 attack.no_flash = GET_INT_ARG(8);
10637                 attack.pause_add = GET_INT_ARG(9);
10638                 attack_coords.z1 = GET_INT_ARG(10); // depth or z
10639 
10640                 switch(cmd)
10641                 {
10642                 case CMD_MODEL_COLLISION:
10643                 case CMD_MODEL_COLLISION1:
10644                     attack.attack_type = ATK_NORMAL;
10645                     break;
10646                 case CMD_MODEL_COLLISION2:
10647                     attack.attack_type  = ATK_NORMAL2;
10648                     break;
10649                 case CMD_MODEL_COLLISION3:
10650                     attack.attack_type  = ATK_NORMAL3;
10651                     break;
10652                 case CMD_MODEL_COLLISION4:
10653                     attack.attack_type  = ATK_NORMAL4;
10654                     break;
10655                 case CMD_MODEL_COLLISION5:
10656                     attack.attack_type  = ATK_NORMAL5;
10657                     break;
10658                 case CMD_MODEL_COLLISION6:
10659                     attack.attack_type  = ATK_NORMAL6;
10660                     break;
10661                 case CMD_MODEL_COLLISION7:
10662                     attack.attack_type  = ATK_NORMAL7;
10663                     break;
10664                 case CMD_MODEL_COLLISION8:
10665                     attack.attack_type  = ATK_NORMAL8;
10666                     break;
10667                 case CMD_MODEL_COLLISION9:
10668                     attack.attack_type  = ATK_NORMAL9;
10669                     break;
10670                 case CMD_MODEL_COLLISION10:
10671                     attack.attack_type  = ATK_NORMAL10;
10672                     break;
10673                 case CMD_MODEL_SHOCK:
10674                     attack.attack_type  = ATK_SHOCK;
10675                     break;
10676                 case CMD_MODEL_BURN:
10677                     attack.attack_type  = ATK_BURN;
10678                     break;
10679                 case CMD_MODEL_STEAL:
10680                     attack.steal = 1;
10681                     attack.attack_type  = ATK_STEAL;
10682                     break;
10683                 case CMD_MODEL_FREEZE:
10684                     attack.attack_type  = ATK_FREEZE;
10685                     attack.freeze = 1;
10686                     attack.freezetime = GET_FLOAT_ARG(6) * GAME_SPEED;
10687                     attack.forcemap = -1;
10688                     attack.attack_drop = 0;
10689                     break;
10690                 case CMD_MODEL_ITEMBOX:
10691                     attack.attack_type  = ATK_ITEM;
10692                     break;
10693                 default:
10694                     tempInt = atoi(command + 6);
10695                     if(tempInt < MAX_ATKS - STA_ATKS + 1)
10696                     {
10697                         tempInt = MAX_ATKS - STA_ATKS + 1;
10698                     }
10699                     attack.attack_type = tempInt + STA_ATKS - 1;
10700                 }
10701                 break;
10702             case CMD_MODEL_HITWALLTYPE:
10703                 value = GET_ARG(1);
10704                 newchar->hitwalltype = atoi(value);
10705                 break;
10706             case CMD_MODEL_COLLISIONZ:
10707             case CMD_MODEL_HITZ:
10708                 attack_coords.z1 = GET_INT_ARG(1);
10709                 attack_coords.z2 = GET_INT_ARG(2);
10710                 break;
10711             case CMD_MODEL_BLAST:
10712                 abox.x = GET_INT_ARG(1);
10713                 abox.y = GET_INT_ARG(2);
10714                 abox.width = GET_INT_ARG(3);
10715                 abox.height = GET_INT_ARG(4);
10716                 attack.dropv.y = default_model_dropv.y;
10717                 attack.dropv.x = default_model_dropv.x * 2.083f;
10718                 attack.dropv.z = 0;
10719                 attack.attack_force = GET_INT_ARG(5);
10720                 attack.no_block = GET_INT_ARG(6);
10721                 attack.no_flash = GET_INT_ARG(7);
10722                 attack.pause_add = GET_INT_ARG(8);
10723                 attack.attack_drop = 1;
10724                 attack.attack_type = ATK_BLAST;
10725                 attack_coords.z1 = GET_INT_ARG(9); // depth or z
10726                 attack.blast = 1;
10727                 break;
10728             case CMD_MODEL_DROPV:
10729                 // drop velocity add if the target is knocked down
10730                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10731                 pattack->dropv.y = GET_FLOAT_ARG(1); // height add
10732                 pattack->dropv.x = GET_FLOAT_ARG(2); // xdir add
10733                 pattack->dropv.z = GET_FLOAT_ARG(3); // zdir add
10734                 break;
10735             case CMD_MODEL_OTG:
10736                 // Over The Ground hit.
10737                 attack.otg = GET_INT_ARG(1);
10738                 break;
10739             case CMD_MODEL_JUGGLECOST:
10740                 // if cost >= opponents jugglepoints , we can juggle
10741                 attack.jugglecost = GET_INT_ARG(1);
10742                 break;
10743             case CMD_MODEL_GUARDCOST:
10744                 // if cost >= opponents guardpoints , opponent will play guardcrush anim
10745                 attack.guardcost = GET_INT_ARG(1);
10746                 break;
10747             case CMD_MODEL_STUN:
10748                 //Like Freeze, but no auto remap.
10749                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10750                 pattack->freeze = 1;
10751                 pattack->freezetime = GET_FLOAT_ARG(1) * GAME_SPEED;
10752                 pattack->attack_drop = 0;
10753                 break;
10754             case CMD_MODEL_GRABIN:
10755                 // fake grab distanse efffect, not link
10756                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10757                 pattack->grab =  GET_INT_ARG(1);
10758                 pattack->grab_distance = GET_FLOAT_ARG(2);
10759                 break;
10760             case CMD_MODEL_NOREFLECT:
10761                 // only cost target's hp, don't knock down or cause pain, unless the target is killed
10762                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10763                 pattack->no_pain = GET_INT_ARG(1);
10764                 break;
10765             case CMD_MODEL_NOKILL:
10766                 // don't kill the target, leave 1 hp
10767                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10768                 pattack->no_kill = GET_INT_ARG(1);
10769                 break;
10770             case CMD_MODEL_FORCEDIRECTION:
10771                 // the attack direction
10772                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10773                 pattack->force_direction = GET_INT_ARG(1);
10774                 break;
10775             case CMD_MODEL_DAMAGEONLANDING:
10776                 // fake throw damage on landing
10777                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10778                 pattack->damage_on_landing = GET_INT_ARG(1);
10779                 pattack->blast = GET_INT_ARG(2);
10780                 break;
10781             case CMD_MODEL_SEAL:
10782                 // Disable special moves for specified time.
10783                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10784                 pattack->sealtime = GET_INT_ARG(1) * GAME_SPEED;
10785                 pattack->seal = GET_INT_ARG(2);
10786                 break;
10787             case CMD_MODEL_STAYDOWN:
10788                 // Disable special moves for specified time.
10789                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10790                 pattack->staydown.rise          = GET_INT_ARG(1); //Risetime modifier.
10791                 pattack->staydown.riseattack    = GET_INT_ARG(2); //Riseattack time addition and toggle.
10792                 break;
10793             case CMD_MODEL_DOT:
10794 
10795                 recursive.index  = GET_INT_ARG(1);  //Index.
10796                 recursive.time   = GET_INT_ARG(2);  //Time to expiration.
10797                 recursive.mode   = GET_INT_ARG(3);  //Mode, see common_dot.
10798                 recursive.force  = GET_INT_ARG(4);  //Amount per tick.
10799                 recursive.rate   = GET_INT_ARG(5);  //Tick delay.
10800                 break;
10801 
10802             case CMD_MODEL_FORCEMAP:
10803                 // force color map change for specified time
10804                 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
10805                 pattack->forcemap = GET_INT_ARG(1);
10806                 pattack->maptime = GET_FLOAT_ARG(2) * GAME_SPEED;
10807                 break;
10808             case CMD_MODEL_IDLE:
10809                 idle = GET_INT_ARG(1);
10810                 break;
10811             case CMD_MODEL_SETA:
10812                 move.base = GET_INT_ARG(1);
10813                 break;
10814             case CMD_MODEL_MOVE:
10815                 move.x = GET_INT_ARG(1);
10816                 break;
10817             case CMD_MODEL_MOVEA:
10818                 move.y = GET_INT_ARG(1);
10819                 break;
10820             case CMD_MODEL_MOVEZ:
10821                 move.z = GET_INT_ARG(1);
10822                 break;
10823             case CMD_MODEL_FSHADOW:
10824                 frameshadow = GET_INT_ARG(1);
10825                 break;
10826             case CMD_MODEL_RANGE:
10827                 if(!newanim)
10828                 {
10829                     shutdownmessage = "Cannot set range: no animation!";
10830                     goto lCleanup;
10831                 }
10832                 newanim->range.min.x = GET_INT_ARG(1);
10833                 newanim->range.max.x = GET_INT_ARG(2);
10834                 if(newanim->range.min.x == newanim->range.max.x)
10835                 {
10836                     newanim->range.min.x--;
10837                 }
10838                 break;
10839             case CMD_MODEL_RANGEZ:
10840                 if(!newanim)
10841                 {
10842                     shutdownmessage = "Cannot set rangez: no animation!";
10843                     goto lCleanup;
10844                 }
10845                 newanim->range.min.z = GET_INT_ARG(1);
10846                 newanim->range.max.z = GET_INT_ARG(2);
10847                 break;
10848             case CMD_MODEL_RANGEA:
10849                 if(!newanim)
10850                 {
10851                     shutdownmessage = "Cannot set rangea: no animation!";
10852                     goto lCleanup;
10853                 }
10854                 newanim->range.min.y = GET_INT_ARG(1);
10855                 newanim->range.max.y = GET_INT_ARG(2);
10856                 break;
10857             case CMD_MODEL_RANGEB:
10858                 if(!newanim)
10859                 {
10860                     shutdownmessage = "Cannot set rangeb: no animation!";
10861                     goto lCleanup;
10862                 }
10863                 newanim->range.min.base = GET_INT_ARG(1);
10864                 newanim->range.max.base = GET_INT_ARG(2);
10865                 break;
10866             case CMD_MODEL_PATHFINDSTEP:
10867                 newchar->pathfindstep = GET_FLOAT_ARG(1);
10868                 break;
10869             case CMD_MODEL_FRAME:
10870             {
10871                 if(!newanim)
10872                 {
10873                     shutdownmessage = "Cannot add frame: animation not specified!";
10874                     goto lCleanup;
10875                 }
10876                 peek = 0;
10877                 if(frameset && framecount >= 0)
10878                 {
10879                     framecount = -framecount;
10880                 }
10881                 while(!frameset)
10882                 {
10883                     value3 = findarg(buf + pos + peek, 0);
10884                     if(stricmp(value3, "frame") == 0)
10885                     {
10886                         framecount++;
10887                     }
10888                     if((stricmp(value3, "anim") == 0) || (pos + peek >= size))
10889                     {
10890                         frameset = 1;
10891                     }
10892                     // Go to next line
10893                     while(buf[pos + peek] && buf[pos + peek] != '\n' && buf[pos + peek] != '\r')
10894                     {
10895                         ++peek;
10896                     }
10897                     while(buf[pos + peek] == '\n' || buf[pos + peek] == '\r')
10898                     {
10899                         ++peek;
10900                     }
10901                 }
10902                 value = GET_ARG(1);
10903                 //printf("frame count: %d\n",framecount);
10904                 //printf("Load sprite '%s'...\n", value);
10905                 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
10906                 if(index >= 0)
10907                 {
10908                     if(pixelformat == PIXEL_x8 && !nopalette)
10909                     {
10910                         // for old mod just give it a default palette
10911                         if(newchar->palette == NULL)
10912                         {
10913                             newchar->palette = malloc(PAL_BYTES);
10914                             if(loadimagepalette(value, packfile, newchar->palette) == 0)
10915                             {
10916                                 shutdownmessage = "Failed to load palette!";
10917                                 goto lCleanup;
10918                             }
10919                         }
10920                         if(!nopalette)
10921                         {
10922                             sprite_map[index].node->sprite->palette = newchar->palette;
10923                             sprite_map[index].node->sprite->pixelformat = pixelformat;
10924                         }
10925                     }
10926                     if(maskindex >= 0)
10927                     {
10928                         sprite_map[index].node->sprite->mask = sprite_map[maskindex].node->sprite;
10929                         maskindex = -1;
10930                     }
10931                 }
10932                 // Adjust coords: add offsets and change size to coords
10933                 body_coords.x      = bbox.x - offset.x;
10934                 body_coords.y      = bbox.y - offset.y;
10935                 body_coords.width  = bbox.width + body_coords.x;
10936                 body_coords.height = bbox.height + body_coords.y;
10937                 body_coords.z1     = bbox.z1;
10938                 body_coords.z2     = bbox.z2;
10939 
10940                 if(body_coords.z2 > body_coords.z1)
10941                 {
10942                     body_coords.z1 -= offset.y;
10943                     body_coords.z2 -= offset.y;
10944                 }
10945 
10946                 attack_coords.x      = abox.x - offset.x;
10947                 attack_coords.y      = abox.y - offset.y;
10948                 attack_coords.width  = abox.width + attack_coords.x;
10949                 attack_coords.height = abox.height + attack_coords.y;
10950 
10951                 if(attack_coords.z2 > attack_coords.z1)
10952                 {
10953                     attack_coords.z1 -= offset.y;
10954                     attack_coords.z2 -= offset.y;
10955                 }
10956 
10957                 //attack.coords.z1 = abox.z1;
10958                 if(platform[0] == 99999) // old style
10959                 {
10960                     platform_con[0] = 0;
10961                     platform_con[1] = 3;
10962                     platform_con[2] = platform[2] - offset.x;
10963                     platform_con[3] = platform[3] - offset.x;
10964                     platform_con[4] = platform[4] - offset.x;
10965                     platform_con[5] = platform[5] - offset.x;
10966                     platform_con[6] = platform[6] + 3;
10967                 }
10968                 else // wall style
10969                 {
10970                     platform_con[0] = platform[0] - offset.x;
10971                     platform_con[1] = platform[1] - offset.y;
10972                     platform_con[2] = platform[2];
10973                     platform_con[3] = platform[3];
10974                     platform_con[4] = platform[4];
10975                     platform_con[5] = platform[5];
10976                     platform_con[6] = platform[6];
10977                 }
10978                 platform_con[7] = platform[7];
10979                 if(shadow_set)
10980                 {
10981                     shadow_coords[0] = shadow_xz[0] - offset.x;
10982                     shadow_coords[1] = shadow_xz[1] - offset.y;
10983                 }
10984                 else
10985                 {
10986                     shadow_coords[0] = shadow_coords[1] = 0;
10987                 }
10988 
10989                 if(drawmethod.flag)
10990                 {
10991                     dm = drawmethod;
10992                     if(dm.clipw)
10993                     {
10994                         dm.clipx -= offset.x;
10995                         dm.clipy -= offset.y;
10996                     }
10997                 }
10998                 else
10999                 {
11000                     dm.flag = 0;
11001                 }
11002 
11003                 curframe = addframe(newanim, index, framecount, delay, idle,
11004                                     &bbox_con, &attack, &move, platform_con,
11005                                     frameshadow, shadow_coords, soundtoplay,
11006                                     &dm, &offset, &recursive, &attack_coords,
11007                                     &body_coords);
11008 
11009                 soundtoplay = -1;
11010                 frm_id = -1;
11011             }
11012             break;
11013             case CMD_MODEL_ALPHAMASK:
11014                 if(!newanim)
11015                 {
11016                     shutdownmessage = "Cannot add alpha mask: animation not specified!";
11017                     goto lCleanup;
11018                 }
11019                 if(maskindex >= 0)
11020                 {
11021                     shutdownmessage = "Cannot add alpha mask: a mask has already been specified for this frame!";
11022                     goto lCleanup;
11023                 }
11024                 value = GET_ARG(1);
11025                 //printf("frame count: %d\n",framecount);
11026                 //printf("Load sprite '%s'...\n", value);
11027                 index = loadsprite(value, offset.x, offset.y, PIXEL_8); //don't use palette for the mask
11028                 maskindex = index;
11029                 break;
11030             case CMD_MODEL_FLIPFRAME:
11031                 newanim->flipframe = GET_FRAME_ARG(1);
11032                 break;
11033             case CMD_MODEL_FOLLOWANIM:
11034                 newanim->followup.animation = GET_INT_ARG(1);
11035                 if(newanim->followup.animation > max_follows)
11036                 {
11037                     newanim->followup.animation = max_follows;
11038                 }
11039                 if(newanim->followup.animation < 0)
11040                 {
11041                     newanim->followup.animation = 0;
11042                 }
11043                 break;
11044             case CMD_MODEL_FOLLOWCOND:
11045                 newanim->followup.condition = GET_INT_ARG(1);
11046                 break;
11047             case CMD_MODEL_COUNTERRANGE:
11048                 newanim->counterrange    = malloc(sizeof(*newanim->counterrange));
11049                 memset(newanim->counterrange, 0, sizeof(*newanim->counterrange));
11050 
11051                 newanim->counterrange->frame.min    = GET_FRAME_ARG(1);
11052                 newanim->counterrange->frame.max    = GET_FRAME_ARG(2);
11053                 newanim->counterrange->condition    = GET_INT_ARG(3);
11054                 newanim->counterrange->damaged      = GET_INT_ARG(4);
11055                 break;
11056             case CMD_MODEL_WEAPONFRAME:
11057                 if(!newanim->weaponframe)
11058                 {
11059                     newanim->weaponframe = malloc(sizeof(*newanim->weaponframe) * 3);
11060                 }
11061                 newanim->weaponframe[0] = GET_FRAME_ARG(1);
11062                 newanim->weaponframe[1] = GET_INT_ARG(2);
11063                 newanim->weaponframe[2] = GET_INT_ARG(3);
11064                 break;
11065             case CMD_MODEL_QUAKEFRAME:
11066                 newanim->quakeframe.framestart  = GET_FRAME_ARG(1);
11067                 newanim->quakeframe.repeat      = GET_INT_ARG(2);
11068                 newanim->quakeframe.v           = GET_INT_ARG(3);
11069                 newanim->quakeframe.cnt         = 0;
11070                 break;
11071             case CMD_MODEL_SUBENTITY:
11072             case CMD_MODEL_CUSTENTITY:
11073                 value = GET_ARG(1);
11074                 if(value[0])
11075                 {
11076                     newanim->subentity = get_cached_model_index(value);
11077                 }
11078                 break;
11079             case CMD_MODEL_SPAWNFRAME:
11080                 newanim->spawnframe    = malloc(5 * sizeof(*newanim->spawnframe));
11081                 memset(newanim->spawnframe, 0, 5 * sizeof(*newanim->spawnframe));
11082                 newanim->spawnframe[0] = GET_FRAME_ARG(1);
11083                 newanim->spawnframe[1] = GET_FLOAT_ARG(2);
11084                 newanim->spawnframe[2] = GET_FLOAT_ARG(3);
11085                 newanim->spawnframe[3] = GET_FLOAT_ARG(4);
11086                 newanim->spawnframe[4] = GET_FLOAT_ARG(5);
11087                 break;
11088             case CMD_MODEL_SUMMONFRAME:
11089                 newanim->summonframe    = malloc(5 * sizeof(*newanim->summonframe));
11090                 memset(newanim->summonframe, 0, 5 * sizeof(*newanim->summonframe));
11091                 newanim->summonframe[0] = GET_FRAME_ARG(1);
11092                 newanim->summonframe[1] = GET_FLOAT_ARG(2);
11093                 newanim->summonframe[2] = GET_FLOAT_ARG(3);
11094                 newanim->summonframe[3] = GET_FLOAT_ARG(4);
11095                 newanim->summonframe[4] = GET_FLOAT_ARG(5);
11096                 break;
11097             case CMD_MODEL_UNSUMMONFRAME:
11098                 newanim->unsummonframe = GET_FRAME_ARG(1);
11099                 break;
11100             case CMD_MODEL_NOHITHEAD:
11101                 value = GET_ARG(1);
11102                 newchar->nohithead = atoi(value);
11103                 break;
11104             case CMD_MODEL_AT_SCRIPT:
11105                 if(!scriptbuf[0])  // if empty, paste the main function text here
11106                 {
11107                     buffer_append(&scriptbuf, pre_text, 0xffffff, &sbsize, &scriptlen);
11108                 }
11109                 scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars
11110                 scriptlen = strlen(scriptbuf);
11111                 if(ani_id >= 0)
11112                 {
11113                     if(script_id != ani_id)  // if expression 1
11114                     {
11115                         sprintf(namebuf, ifid_text, newanim->index);
11116                         buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11117                         script_id = ani_id;
11118                     }
11119                     scriptbuf[scriptlen - strclen(endifid_text)] = 0; // cut last chars
11120                     scriptlen = strlen(scriptbuf);
11121                 }
11122                 while(!starts_with(buf + pos, "@script"))
11123                 {
11124                     pos++;
11125                 }
11126                 pos += strclen("@script");
11127                 len = 0;
11128                 while(!starts_with(buf + pos, "@end_script"))
11129                 {
11130                     len++;
11131                     pos++;
11132                 }
11133                 buffer_append(&scriptbuf, buf + pos - len, len, &sbsize, &scriptlen);
11134                 pos += strclen("@end_script");
11135 
11136                 if(ani_id >= 0)
11137                 {
11138                     buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen);// put back last  chars
11139                 }
11140                 buffer_append(&scriptbuf, sur_text, 0xffffff, &sbsize, &scriptlen);// put back last  chars
11141                 break;
11142             case CMD_MODEL_AT_CMD:
11143                 //translate @cmd into script function call
11144                 if(ani_id < 0)
11145                 {
11146                     shutdownmessage = "command '@cmd' must follow an animation!";
11147                     goto lCleanup;
11148                 }
11149                 if(!scriptbuf[0])  // if empty, paste the main function text here
11150                 {
11151                     buffer_append(&scriptbuf, pre_text, 0xffffff, &sbsize, &scriptlen);
11152                 }
11153                 scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars
11154                 scriptlen = strlen(scriptbuf);
11155                 if(script_id != ani_id)  // if expression 1
11156                 {
11157                     sprintf(namebuf, ifid_text, newanim->index);
11158                     buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11159                     script_id = ani_id;
11160                 }
11161                 j = 1;
11162                 value = GET_ARG(j);
11163                 scriptbuf[scriptlen - strclen(endifid_text)] = 0; // cut last chars
11164                 scriptlen = strlen(scriptbuf);
11165                 if(value && value[0])
11166                 {
11167                     /*
11168                      //no_cmd_compatible will try to optimize if(frame==n)
11169                      //which means merging extra if statements within the same frame
11170                      //some old mod will have problem if this is enabled, however.
11171                      //
11172                      //     @cmd f
11173                      //     @cmd f
11174                      //     frame
11175                      //
11176                      //   When no_cmd_compatible is 1
11177                      //
11178                      //   if(frame==n) {
11179                      //       f();
11180                      //       f();
11181                      //       return;
11182                      //    }
11183                      //
11184                      //    When no_cmd_compatible is 0
11185                      //
11186                      //   if(frame==n) {
11187                      //       f();
11188                      //    }
11189                      //   if(frame==n) {
11190                      //       f();
11191                      //    }
11192                      */
11193                     if(!no_cmd_compatible || frm_id != curframe)
11194                     {
11195                         sprintf(namebuf, if_text, curframe);//only execute in current frame
11196                         buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11197                         frm_id = curframe;
11198                     }
11199                     else //no_cmd_compatible==1
11200                     {
11201                         scriptbuf[scriptlen - strclen(endif_text)] = 0; // cut last chars
11202                         scriptlen = strlen(scriptbuf);
11203                         scriptbuf[scriptlen - strclen(endif_return_text)] = 0; // cut last chars
11204                         scriptlen = strlen(scriptbuf);
11205                     }
11206                     sprintf(namebuf, call_text, value);
11207                     buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen);
11208                     do  //argument and comma
11209                     {
11210                         j++;
11211                         value = GET_ARG(j);
11212                         if(value && value[0])
11213                         {
11214                             if(j != 2)
11215                             {
11216                                 buffer_append(&scriptbuf, comma_text, 0xffffff, &sbsize, &scriptlen);
11217                             }
11218                             buffer_append(&scriptbuf, value, 0xffffff, &sbsize, &scriptlen);
11219                         }
11220                     }
11221                     while(value && value[0]);
11222                 }
11223                 buffer_append(&scriptbuf, endcall_text, 0xffffff, &sbsize, &scriptlen);
11224                 if(no_cmd_compatible)
11225                 {
11226                     buffer_append(&scriptbuf, endif_return_text, 0xffffff, &sbsize, &scriptlen);    //return
11227                 }
11228                 buffer_append(&scriptbuf, endif_text, 0xffffff, &sbsize, &scriptlen);//end of if
11229                 buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen); // put back last  chars
11230                 buffer_append(&scriptbuf, sur_text, 0xffffff, &sbsize, &scriptlen); // put back last  chars
11231                 break;
11232             default:
11233                 if(command && command[0])
11234                 {
11235                     if(!handle_txt_include(command, &arglist, &filename, fnbuf, &buf, &pos, &size))
11236                     {
11237                         printf("Command '%s' not understood in file '%s'!\n", command, filename);
11238                     }
11239                 }
11240             }
11241 
11242         }
11243         // Go to next line
11244         pos += getNewLineStart(buf + pos);
11245     }
11246 
11247 
11248     tempInt = 1;
11249 
11250     if(scriptbuf && animscriptbuf && scriptbuf[0] && animscriptbuf[0])
11251     {
11252         writeToScriptLog("\n#### animationscript function main #####\n# ");
11253         writeToScriptLog(filename);
11254         writeToScriptLog("\n########################################\n");
11255         writeToScriptLog(scriptbuf);
11256 
11257         lcmScriptDeleteMain(&scriptbuf);
11258         lcmScriptAddMain(&animscriptbuf);
11259         lcmScriptJoinMain(&animscriptbuf,scriptbuf);
11260 
11261         if(!Script_IsInitialized(newchar->scripts->animation_script))
11262         {
11263             Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0);
11264         }
11265         tempInt = Script_AppendText(newchar->scripts->animation_script, animscriptbuf, filename);
11266     }
11267     else if(animscriptbuf && animscriptbuf[0])
11268     {
11269         lcmScriptAddMain(&animscriptbuf);
11270 
11271         if(!Script_IsInitialized(newchar->scripts->animation_script))
11272         {
11273             Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0);
11274         }
11275         tempInt = Script_AppendText(newchar->scripts->animation_script, animscriptbuf, filename);
11276     }
11277     else if(scriptbuf && scriptbuf[0])
11278     {
11279         //printf("\n%s\n", scriptbuf);
11280         if(!Script_IsInitialized(newchar->scripts->animation_script))
11281         {
11282             Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0);
11283         }
11284         tempInt = Script_AppendText(newchar->scripts->animation_script, scriptbuf, filename);
11285         //Interpreter_OutputPCode(newchar->scripts->animation_script.pinterpreter, "code");
11286         writeToScriptLog("\n#### animationscript function main #####\n# ");
11287         writeToScriptLog(filename);
11288         writeToScriptLog("\n########################################\n");
11289         writeToScriptLog(scriptbuf);
11290     }
11291 
11292     if(!newchar->isSubclassed)
11293     {
11294         Script_Compile(newchar->scripts->animation_script);
11295     }
11296 
11297     if(!tempInt)// parse script failed
11298     {
11299         shutdownmessage = "Error parsing function main of animation script in file '%s'!";
11300         goto lCleanup;
11301     }
11302 
11303     // We need a little more work to initialize the new A.I. types if they are not loaded from file
11304     if(newchar->aiattack == -1)
11305     {
11306         newchar->aiattack = 0;
11307     }
11308     if(newchar->aimove == -1)
11309     {
11310         newchar->aimove = 0;
11311     }
11312     //if(!newchar->offscreenkill) newchar->offscreenkill = 1000;
11313 
11314     //temporary patch for conflicting moves
11315     if(newchar->animation[ANI_FREESPECIAL] && !is_set(newchar, ANI_FREESPECIAL))
11316     {
11317         alloc_specials(newchar);
11318         newchar->special[newchar->specials_loaded].input[0] = FLAG_FORWARD;
11319         newchar->special[newchar->specials_loaded].input[1] = FLAG_FORWARD;
11320         newchar->special[newchar->specials_loaded].input[2] = FLAG_ATTACK;
11321         newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL;
11322         newchar->special[newchar->specials_loaded].steps = 3;
11323         newchar->specials_loaded++;
11324     }
11325     if(newchar->animation[ANI_FREESPECIAL2] && !is_set(newchar, ANI_FREESPECIAL2))
11326     {
11327         alloc_specials(newchar);
11328         newchar->special[newchar->specials_loaded].input[0] = FLAG_MOVEDOWN;
11329         newchar->special[newchar->specials_loaded].input[1] = FLAG_MOVEDOWN;
11330         newchar->special[newchar->specials_loaded].input[2] = FLAG_ATTACK;
11331         newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL2;
11332         newchar->special[newchar->specials_loaded].steps = 3;
11333         newchar->specials_loaded++;
11334     }
11335     if(newchar->animation[ANI_FREESPECIAL3] && !is_set(newchar, ANI_FREESPECIAL3))
11336     {
11337         alloc_specials(newchar);
11338         newchar->special[newchar->specials_loaded].input[0] = FLAG_MOVEUP;
11339         newchar->special[newchar->specials_loaded].input[1] = FLAG_MOVEUP;
11340         newchar->special[newchar->specials_loaded].input[2] = FLAG_ATTACK;
11341         newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL3;
11342         newchar->special[newchar->specials_loaded].steps = 3;
11343         newchar->specials_loaded++;
11344     }
11345 
11346     if(newchar->risetime.rise == -1)
11347     {
11348         if(newchar->type == TYPE_PLAYER)
11349         {
11350             if(newchar->animation[ANI_RISEATTACK])
11351             {
11352                 newchar->risetime.rise = GAME_SPEED / 2;
11353             }
11354             else
11355             {
11356                 newchar->risetime.rise = GAME_SPEED;
11357             }
11358         }
11359         else if(newchar->type == TYPE_ENEMY || newchar->type == TYPE_NPC)
11360         {
11361             newchar->risetime.rise = 0;
11362         }
11363     }
11364 
11365     if(newchar->hostile < 0) // not been initialized, so initialize it
11366     {
11367         switch (newchar->type)
11368         {
11369         default:
11370             //Do nothing.
11371             break;
11372         case TYPE_ENEMY:
11373             newchar->hostile = TYPE_PLAYER ;
11374             break;
11375         case TYPE_PLAYER: // dont really needed, since you don't need A.I. control for players
11376             newchar->hostile = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
11377             break;
11378         case TYPE_TRAP:
11379             newchar->hostile  = TYPE_ENEMY | TYPE_PLAYER;
11380         case TYPE_OBSTACLE:
11381             newchar->hostile = 0;
11382             break;
11383         case TYPE_SHOT:  // only target enemies
11384             newchar->hostile = TYPE_ENEMY ;
11385             break;
11386         case TYPE_NPC: // default npc behivior
11387             newchar->hostile = TYPE_ENEMY ;
11388             break;
11389         }
11390     }
11391 
11392     if(newchar->candamage < 0) // not been initialized, so initialize it
11393     {
11394         switch (newchar->type)
11395         {
11396         default:
11397             //Do nothing.
11398             break;
11399         case TYPE_ENEMY:
11400             newchar->candamage = TYPE_PLAYER | TYPE_SHOT;
11401             if(newchar->subtype == SUBTYPE_ARROW || newchar->subtype == SUBTYPE_BOOMERANG)
11402             {
11403                 newchar->candamage |= TYPE_OBSTACLE;
11404             }
11405             break;
11406         case TYPE_PLAYER:
11407             newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
11408             break;
11409         case TYPE_TRAP:
11410             newchar->candamage  = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11411         case TYPE_OBSTACLE:
11412             newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
11413             break;
11414         case TYPE_SHOT:
11415             newchar->candamage = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11416             break;
11417         case TYPE_NPC:
11418             newchar->candamage = TYPE_ENEMY | TYPE_OBSTACLE;
11419             break;
11420         case TYPE_ITEM:
11421             newchar->candamage = TYPE_PLAYER;
11422             break;
11423         }
11424     }
11425 
11426     if(newchar->projectilehit < 0) // not been initialized, so initialize it
11427     {
11428         switch (newchar->type)
11429         {
11430         default:
11431             //Do nothing.
11432             break;
11433         case TYPE_ENEMY:
11434             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11435             break;
11436         case TYPE_PLAYER:
11437             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11438             break;
11439         case TYPE_TRAP: // hmm, don't really needed
11440             newchar->projectilehit  = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11441         case TYPE_OBSTACLE: // hmm, don't really needed
11442             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11443             break;
11444         case TYPE_SHOT: // hmm, don't really needed
11445             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11446             break;
11447         case TYPE_NPC:
11448             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
11449             break;
11450         }
11451     }
11452 
11453     if(newchar->jumpspeed < 0)
11454     {
11455         newchar->jumpspeed = MAX(newchar->speed, 1);
11456     }
11457 
11458     if(blendfx_is_set == 0)
11459     {
11460         if(newchar->alpha)
11461         {
11462             blendfx[newchar->alpha - 1] = 1;
11463         }
11464         if(newchar->gfxshadow || newchar->shadow)
11465         {
11466             blendfx[BLEND_MULTIPLY] = 1;
11467         }
11468     }
11469 
11470     // we need to convert 8bit colourmap into 24bit palette
11471     if(pixelformat == PIXEL_x8)
11472     {
11473         convert_map_to_palette(newchar, mapflag);
11474     }
11475 
11476     printf("Loading '%s' from %s\n", newchar->name, filename);
11477 
11478 lCleanup:
11479 
11480     if(buf != NULL)
11481     {
11482         free(buf);
11483         buf = NULL;
11484     }
11485     if(scriptbuf)
11486     {
11487         free(scriptbuf);
11488         scriptbuf = NULL;
11489     }
11490     if(animscriptbuf)
11491     {
11492         free(animscriptbuf);
11493         animscriptbuf = NULL;
11494     }
11495     if(mapflag)
11496     {
11497         free(mapflag);
11498         mapflag = NULL;
11499     }
11500 
11501     if(!shutdownmessage)
11502     {
11503         return newchar;
11504     }
11505 
11506     shutdown(1, "Fatal Error in load_cached_model, file: %s, line %d, message: %s\n", filename, line, shutdownmessage);
11507     return NULL;
11508 }
11509 
11510 
11511 
is_set(s_model * model,int m)11512 int is_set(s_model *model, int m)      // New function to determine if a freespecial has been set
11513 {
11514     int i;
11515 
11516     for(i = 0; i < model->specials_loaded; i++)
11517     {
11518         if(model->special[i].anim == m)
11519         {
11520             return 1;
11521         }
11522     }
11523 
11524     return 0;
11525 }
11526 
load_script_setting()11527 int load_script_setting()
11528 {
11529     char *filename = "data/script.txt";
11530     char *buf, *command;
11531     ptrdiff_t pos = 0;
11532     size_t size = 0;
11533     ArgList arglist;
11534     char argbuf[MAX_ARG_LEN + 1] = "";
11535 
11536     if(buffer_pakfile(filename, &buf, &size) != 1)
11537     {
11538         return 0;
11539     }
11540 
11541     while(pos < size)
11542     {
11543         if(ParseArgs(&arglist, buf + pos, argbuf))
11544         {
11545             command = GET_ARG(0);
11546             if(command && command[0])
11547             {
11548                 if(stricmp(command, "maxscriptvars") == 0) // each script can have a variable list that can be accessed by index
11549                 {
11550                     max_script_vars = GET_INT_ARG(1) ;
11551                     if(max_script_vars < 0)
11552                     {
11553                         max_script_vars = 0;
11554                     }
11555                 }
11556                 else if(stricmp(command, "maxentityvars") == 0) // each entity can have a variable list that can be accessed by index
11557                 {
11558                     max_entity_vars = GET_INT_ARG(1) ;
11559                     if(max_entity_vars < 0)
11560                     {
11561                         max_entity_vars = 0;
11562                     }
11563                 }
11564                 else if(stricmp(command, "maxindexedvars") == 0) // a global variable list that can be accessed by index
11565                 {
11566                     max_indexed_vars = GET_INT_ARG(1);
11567                     if(max_indexed_vars < 0)
11568                     {
11569                         max_indexed_vars = 0;
11570                     }
11571                 }
11572                 else if(stricmp(command, "keyscriptrate") == 0) // Rate that keyscripts fire when holding a key.
11573                 {
11574                     keyscriptrate = GET_INT_ARG(1);
11575                 }
11576                 else if(stricmp(command, "alwaysupdate") == 0) //execute update script whenever update() is called
11577                 {
11578                     alwaysupdate = GET_INT_ARG(1);
11579                 }
11580                 else if(stricmp(command, "nonestedscript") == 0) // don't call a script if it is being executed
11581                 {
11582                     no_nested_script = GET_INT_ARG(1);
11583                 }
11584                 else if(stricmp(command, "nocmdcompatible") == 0) // don't call a script if it is being executed
11585                 {
11586                     no_cmd_compatible = GET_INT_ARG(1);
11587                 }
11588             }
11589         }
11590         // Go to next line
11591         pos += getNewLineStart(buf + pos);
11592     }
11593 
11594     if(buf != NULL)
11595     {
11596         free(buf);
11597         buf = NULL;
11598     }
11599     return 1;
11600 }
11601 
load_model_constants()11602 void load_model_constants()
11603 {
11604     char filename[128] = "data/models.txt";
11605     int i;
11606     char *buf;
11607     size_t size;
11608     ptrdiff_t pos;
11609     char *command;
11610     int line = 0;
11611     int maxanim = MAX_ANIS; // temporary counter
11612     ArgList arglist;
11613     char argbuf[MAX_ARG_LEN + 1] = "";
11614     modelstxtCommands cmd;
11615 
11616     // reload default values
11617     max_idles        = MAX_IDLES;
11618     max_walks        = MAX_WALKS;
11619     max_ups          = MAX_UPS;
11620     max_downs        = MAX_DOWNS;
11621     max_backwalks    = MAX_BACKWALKS;
11622     max_attack_types = MAX_ATKS;
11623     max_freespecials = MAX_SPECIALS;
11624     max_follows      = MAX_FOLLOWS;
11625     max_attacks      = MAX_ATTACKS;
11626     max_animations   = MAX_ANIS;
11627 
11628     max_collisons    = MAX_COLLISIONS;
11629 
11630     // free old values
11631     if(animspecials)
11632     {
11633         free(animspecials);
11634         animspecials = NULL;
11635     }
11636     if(animattacks)
11637     {
11638         free(animattacks);
11639         animattacks = NULL;
11640     }
11641     if(animfollows)
11642     {
11643         free(animfollows);
11644         animfollows = NULL;
11645     }
11646     if(animpains)
11647     {
11648         free(animpains);
11649         animpains = NULL;
11650     }
11651     if(animbackpains)
11652     {
11653         free(animbackpains);
11654         animbackpains = NULL;
11655     }
11656     if(animfalls)
11657     {
11658         free(animfalls);
11659         animfalls = NULL;
11660     }
11661     if(animbackfalls)
11662     {
11663         free(animbackfalls);
11664         animbackfalls = NULL;
11665     }
11666     if(animrises)
11667     {
11668         free(animrises);
11669         animrises = NULL;
11670     }
11671     if(animriseattacks)
11672     {
11673         free(animriseattacks);
11674         animriseattacks = NULL;
11675     }
11676     if(animblkpains)
11677     {
11678         free(animblkpains);
11679         animblkpains = NULL;
11680     }
11681     if(animdies)
11682     {
11683         free(animdies);
11684         animdies = NULL;
11685     }
11686     if(animbackdies)
11687     {
11688         free(animbackdies);
11689         animbackdies = NULL;
11690     }
11691     if(animwalks)
11692     {
11693         free(animwalks);
11694         animwalks = NULL;
11695     }
11696     if(animbackwalks)
11697     {
11698         free(animbackwalks);
11699         animbackwalks = NULL;
11700     }
11701     if(animidles)
11702     {
11703         free(animidles);
11704         animidles = NULL;
11705     }
11706     if(animups)
11707     {
11708         free(animups);
11709         animups = NULL;
11710     }
11711     if(animdowns)
11712     {
11713         free(animdowns);
11714         animdowns = NULL;
11715     }
11716 
11717     // Read file
11718     if(buffer_pakfile(filename, &buf, &size) != 1)
11719     {
11720         shutdown(1, "Error loading model list from %s", filename);
11721     }
11722 
11723     pos = 0;
11724     while(pos < size) // peek global settings
11725     {
11726         line++;
11727         if(ParseArgs(&arglist, buf + pos, argbuf))
11728         {
11729             command = GET_ARG(0);
11730             cmd = getModelCommand(modelstxtcmdlist, command);
11731             switch(cmd)
11732             {
11733             case CMD_MODELSTXT_MAX_COLLISIONS:
11734                 // max collision_attack boxes.
11735                 max_collisons = GET_INT_ARG(1);
11736                 if(max_collisons < MAX_COLLISIONS)
11737                 {
11738                     max_collisons = MAX_COLLISIONS;
11739                 }
11740                 break;
11741             case CMD_MODELSTXT_MAXIDLES:
11742                 // max idle stances
11743                 max_idles = GET_INT_ARG(1);
11744                 if(max_idles < MAX_IDLES)
11745                 {
11746                     max_idles = MAX_IDLES;
11747                 }
11748                 break;
11749             case CMD_MODELSTXT_MAXWALKS:
11750                 max_walks = GET_INT_ARG(1);
11751                 if(max_walks < MAX_WALKS)
11752                 {
11753                     max_walks = MAX_WALKS;
11754                 }
11755                 break;
11756             case CMD_MODELSTXT_MAXBACKWALKS:
11757                 // max backward walks
11758                 max_backwalks = GET_INT_ARG(1);
11759                 if(max_backwalks < MAX_BACKWALKS)
11760                 {
11761                     max_backwalks = MAX_BACKWALKS;
11762                 }
11763                 break;
11764             case CMD_MODELSTXT_MAXUPS:
11765                 // max up walks
11766                 max_ups = GET_INT_ARG(1);
11767                 if(max_ups < MAX_UPS)
11768                 {
11769                     max_ups = MAX_UPS;
11770                 }
11771                 break;
11772             case CMD_MODELSTXT_MAXDOWNS:
11773                 // max down walks
11774                 max_downs = GET_INT_ARG(1);
11775                 if(max_downs < MAX_DOWNS)
11776                 {
11777                     max_downs = MAX_DOWNS;
11778                 }
11779                 break;
11780             case CMD_MODELSTXT_MAXATTACKTYPES:
11781                 // max attacktype/pain/fall/die
11782                 max_attack_types = GET_INT_ARG(1) + STA_ATKS;
11783                 if(max_attack_types < MAX_ATKS)
11784                 {
11785                     max_attack_types = MAX_ATKS;
11786                 }
11787                 break;
11788             case CMD_MODELSTXT_MAXFOLLOWS:
11789                 // max follow-ups
11790                 max_follows = GET_INT_ARG(1);
11791                 if(max_follows < MAX_FOLLOWS)
11792                 {
11793                     max_follows = MAX_FOLLOWS;
11794                 }
11795                 break;
11796             case CMD_MODELSTXT_MAXFREESPECIALS:
11797                 // max freespecials
11798                 max_freespecials = GET_INT_ARG(1);
11799                 if(max_freespecials < MAX_SPECIALS)
11800                 {
11801                     max_freespecials = MAX_SPECIALS;
11802                 }
11803                 break;
11804             case CMD_MODELSTXT_MAXATTACKS:
11805                 max_attacks = GET_INT_ARG(1);
11806                 if(max_attacks < MAX_ATTACKS)
11807                 {
11808                     max_attacks = MAX_ATTACKS;
11809                 }
11810                 break;
11811             default:
11812                 if(cmd >= CMD_MODELSTXT_THE_END)
11813                 {
11814                     printf("command %s not understood in %s, line %d\n", command, filename, line);
11815                 }
11816                 break;
11817             }
11818         }
11819 
11820         // Go to next line
11821         pos += getNewLineStart(buf + pos);
11822     }
11823 
11824     // calculate max animations
11825     max_animations += (max_attack_types - MAX_ATKS) * 9 +// multply by 5, for fall/die/pain/backpain/backfalls/backdies/rise/blockpain/riseattack
11826                       (max_follows - MAX_FOLLOWS) +
11827                       (max_freespecials - MAX_SPECIALS) +
11828                       (max_attacks - MAX_ATTACKS) +
11829                       (max_idles - MAX_IDLES) +
11830                       (max_walks - MAX_WALKS) +
11831                       (max_ups - MAX_UPS) +
11832                       (max_downs - MAX_DOWNS) +
11833                       (max_backwalks - MAX_BACKWALKS);
11834 
11835     // alloc indexed animation ids
11836     animdowns = malloc(sizeof(*animdowns) * max_downs);
11837     animups = malloc(sizeof(*animups) * max_ups);
11838     animbackwalks = malloc(sizeof(*animbackwalks) * max_backwalks);
11839     animwalks = malloc(sizeof(*animwalks) * max_walks);
11840     animidles = malloc(sizeof(*animidles) * max_idles);
11841     animpains = malloc(sizeof(*animpains) * max_attack_types);
11842     animbackpains = malloc(sizeof(*animbackpains) * max_attack_types);
11843     animdies = malloc(sizeof(*animdies) * max_attack_types);
11844     animbackdies = malloc(sizeof(*animbackdies) * max_attack_types);
11845     animfalls = malloc(sizeof(*animfalls) * max_attack_types);
11846     animbackfalls = malloc(sizeof(*animbackfalls) * max_attack_types);
11847     animrises = malloc(sizeof(*animrises) * max_attack_types);
11848     animriseattacks = malloc(sizeof(*animriseattacks) * max_attack_types);
11849     animblkpains = malloc(sizeof(*animblkpains) * max_attack_types);
11850     animattacks = malloc(sizeof(*animattacks) * max_attacks);
11851     animfollows = malloc(sizeof(*animfollows) * max_follows);
11852     animspecials = malloc(sizeof(*animspecials) * max_freespecials);
11853 
11854     // copy default values and new animation ids
11855     memcpy(animdowns, downs, sizeof(*animdowns)*MAX_DOWNS);
11856     for(i = MAX_DOWNS; i < max_downs; i++)
11857     {
11858         animdowns[i] = maxanim++;
11859     }
11860     memcpy(animups, ups, sizeof(*animups)*MAX_UPS);
11861     for(i = MAX_UPS; i < max_ups; i++)
11862     {
11863         animups[i] = maxanim++;
11864     }
11865     memcpy(animbackwalks, backwalks, sizeof(*animbackwalks)*MAX_BACKWALKS);
11866     for(i = MAX_BACKWALKS; i < max_backwalks; i++)
11867     {
11868         animbackwalks[i] = maxanim++;
11869     }
11870     memcpy(animwalks, walks, sizeof(*animwalks)*MAX_WALKS);
11871     for(i = MAX_WALKS; i < max_walks; i++)
11872     {
11873         animwalks[i] = maxanim++;
11874     }
11875     memcpy(animidles, idles, sizeof(*animidles)*MAX_IDLES);
11876     for(i = MAX_IDLES; i < max_idles; i++)
11877     {
11878         animidles[i] = maxanim++;
11879     }
11880     memcpy(animspecials, freespecials,   sizeof(*animspecials)*MAX_SPECIALS);
11881     for(i = MAX_SPECIALS; i < max_freespecials; i++)
11882     {
11883         animspecials[i] = maxanim++;
11884     }
11885     memcpy(animattacks,  normal_attacks, sizeof(*animattacks)*MAX_ATTACKS);
11886     for(i = MAX_ATTACKS; i < max_attacks; i++)
11887     {
11888         animattacks[i] = maxanim++;
11889     }
11890     memcpy(animfollows,  follows,        sizeof(*animfollows)*MAX_FOLLOWS);
11891     for(i = MAX_FOLLOWS; i < max_follows; i++)
11892     {
11893         animfollows[i] = maxanim++;
11894     }
11895     memcpy(animpains,    pains,          sizeof(*animpains)*MAX_ATKS);
11896     for(i = MAX_ATKS; i < max_attack_types; i++)
11897     {
11898         animpains[i] = maxanim++;
11899     }
11900     memcpy(animbackpains,    backpains,          sizeof(*animbackpains)*MAX_ATKS);
11901     for(i = MAX_ATKS; i < max_attack_types; i++)
11902     {
11903         animbackpains[i] = maxanim++;
11904     }
11905     memcpy(animfalls,    falls,          sizeof(*animfalls)*MAX_ATKS);
11906     for(i = MAX_ATKS; i < max_attack_types; i++)
11907     {
11908         animfalls[i] = maxanim++;
11909     }
11910     memcpy(animbackfalls,    backfalls,          sizeof(*animbackfalls)*MAX_ATKS);
11911     for(i = MAX_ATKS; i < max_attack_types; i++)
11912     {
11913         animbackfalls[i] = maxanim++;
11914     }
11915     memcpy(animrises,    rises,          sizeof(*animrises)*MAX_ATKS);
11916     for(i = MAX_ATKS; i < max_attack_types; i++)
11917     {
11918         animrises[i] = maxanim++;
11919     }
11920     memcpy(animriseattacks,    riseattacks,          sizeof(*animriseattacks)*MAX_ATKS);
11921     for(i = MAX_ATKS; i < max_attack_types; i++)
11922     {
11923         animriseattacks[i] = maxanim++;
11924     }
11925     memcpy(animblkpains,    blkpains,    sizeof(*animblkpains)*MAX_ATKS);
11926     for(i = MAX_ATKS; i < max_attack_types; i++)
11927     {
11928         animblkpains[i] = maxanim++;
11929     }
11930     memcpy(animdies,     deaths,         sizeof(*animdies)*MAX_ATKS);
11931     for(i = MAX_ATKS; i < max_attack_types; i++)
11932     {
11933         animdies[i] = maxanim++;
11934     }
11935     memcpy(animbackdies,     backdeaths,         sizeof(*animbackdies)*MAX_ATKS);
11936     for(i = MAX_ATKS; i < max_attack_types; i++)
11937     {
11938         animbackdies[i] = maxanim++;
11939     }
11940 
11941     if(buf)
11942     {
11943         free(buf);
11944     }
11945 }
11946 
11947 // Load / cache all models
load_models()11948 int load_models()
11949 {
11950     char filename[128] = "data/models.txt";
11951     int i;
11952     char *buf;
11953     size_t size;
11954     ptrdiff_t pos;
11955     char *command;
11956     int line = 0;
11957 
11958     char tmpBuff[128] = {""};
11959 
11960     ArgList arglist;
11961     char argbuf[MAX_ARG_LEN + 1] = "";
11962     modelstxtCommands cmd;
11963     int modelLoadCount = 0;
11964 
11965     free_modelcache();
11966 
11967     if(isLoadingScreenTypeBg(loadingbg[0].set))
11968     {
11969         // New alternative background path for PSP
11970         if(custBkgrds != NULL)
11971         {
11972             strcpy(tmpBuff, custBkgrds);
11973             strncat(tmpBuff, "loading", 7);
11974             load_background(tmpBuff, 0);
11975         }
11976         else
11977         {
11978             load_background("data/bgs/loading", 0);
11979         }
11980         standard_palette(1);
11981     }
11982     if(isLoadingScreenTypeBar(loadingbg[0].set))
11983     {
11984         lifebar_colors();
11985         init_colourtable();
11986     }
11987 
11988     update_loading(&loadingbg[0], -1, 1); // initialize the update screen
11989 
11990     if(custModels != NULL)
11991     {
11992         strcpy(filename, "data/");
11993         strcat(filename, custModels);
11994     }
11995 
11996     // Read file
11997     if(buffer_pakfile(filename, &buf, &size) != 1)
11998     {
11999         shutdown(1, "Error loading model list from %s", filename);
12000     }
12001 
12002     pos = 0;
12003     while(pos < size) // peek global settings
12004     {
12005         line++;
12006         if(ParseArgs(&arglist, buf + pos, argbuf))
12007         {
12008             command = GET_ARG(0);
12009             cmd = getModelCommand(modelstxtcmdlist, command);
12010             switch(cmd)
12011             {
12012             case CMD_MODELSTXT_COMBODELAY:
12013                 combodelay = GET_INT_ARG(1);
12014                 break;
12015             case CMD_MODELSTXT_MUSIC:
12016                 music(GET_ARG(1), 1, atol(GET_ARG(2)));
12017                 break;
12018             case CMD_MODELSTXT_LOAD:
12019                 // Add path to cache list
12020                 modelLoadCount++;
12021                 cache_model(GET_ARG(1), GET_ARG(2), 1);
12022                 break;
12023             case CMD_MODELSTXT_COLOURSELECT:
12024                 // 6-2-2005 if string for colourselect found
12025                 colourselect =  GET_INT_ARG(1);          //  6-2-2005
12026                 break;
12027             case CMD_MODELSTXT_SPDIRECTION:
12028                 // Select Player Direction for select player screen
12029                 spdirection[0] =  GET_INT_ARG(1);
12030                 spdirection[1] =  GET_INT_ARG(2);
12031                 spdirection[2] =  GET_INT_ARG(3);
12032                 spdirection[3] =  GET_INT_ARG(4);
12033                 break;
12034             case CMD_MODELSTXT_AUTOLAND:
12035                 // New flag to determine if a player auto lands when thrown by another player (2 completely disables the ability to land)
12036                 autoland = GET_INT_ARG(1);
12037                 break;
12038             case CMD_MODELSTXT_NOLOST:
12039                 // this is use for dont lost your weapon if you grab a enemy flag it to 1 to no drop by tails
12040                 nolost = GET_INT_ARG(1);
12041                 break;
12042             case CMD_MODELSTXT_AJSPECIAL:
12043                 // Flag to determine if a + j executes special
12044                 ajspecial = GET_INT_ARG(1);
12045                 break;
12046             case CMD_MODELSTXT_NOCOST:
12047                 // Nocost set in models.txt
12048                 nocost = GET_INT_ARG(1);
12049                 break;
12050             case CMD_MODELSTXT_NOCHEATS:
12051                 //disable cheat option in menu
12052                 forcecheatsoff =  GET_INT_ARG(1);
12053                 break;
12054             case CMD_MODELSTXT_NODEBUG:
12055                 //disable debug option in menu
12056                 nodebugoptions =  GET_INT_ARG(1);
12057                 break;
12058             case CMD_MODELSTXT_NODROPEN:
12059                 nodropen = 1;
12060                 break;
12061             case CMD_MODELSTXT_NODROPSPAWN:
12062                 nodropspawn = 1;
12063                 break;
12064             case CMD_MODELSTXT_KNOW:
12065                 // Just add path to cache list
12066                 cache_model(GET_ARG(1), GET_ARG(2), 0);
12067                 break;
12068             case CMD_MODELSTXT_NOAIRCANCEL:
12069                 noaircancel = GET_INT_ARG(1);
12070                 break;
12071             case CMD_MODELSTXT_NOMAXRUSHRESET:
12072                 nomaxrushreset[4] = GET_INT_ARG(1);
12073                 break;
12074             case CMD_MODELSTXT_MPBLOCK:
12075                 // Take from MP first?
12076                 mpblock = GET_INT_ARG(1);
12077                 break;
12078             case CMD_MODELSTXT_BLOCKRATIO:
12079                 // Nullify or reduce damage?
12080                 blockratio = GET_INT_ARG(1);
12081                 break;
12082             case CMD_MODELSTXT_NOCHIPDEATH:
12083                 nochipdeath = GET_INT_ARG(1);
12084                 break;
12085             case CMD_MODELSTXT_LIFESCORE:
12086                 lifescore =  GET_INT_ARG(1);
12087                 break;
12088             case CMD_MODELSTXT_CREDSCORE:
12089                 // Number of points needed to earn a 1-up
12090                 credscore =  GET_INT_ARG(1);
12091                 break;
12092             case CMD_MODELSTXT_VERSUSDAMAGE:
12093                 // Number of points needed to earn a credit
12094                 versusdamage =  GET_INT_ARG(1);
12095                 if(versusdamage == 0 || versusdamage == 1)
12096                 {
12097                     savedata.mode = versusdamage ^ 1;
12098                 }
12099                 break;
12100             case CMD_MODELSTXT_DROPV:
12101                 default_model_dropv.y =  GET_FLOAT_ARG(1);
12102                 default_model_dropv.x =  GET_FLOAT_ARG(2);
12103                 default_model_dropv.z =  GET_FLOAT_ARG(3);
12104                 break;
12105             case CMD_MODELSTXT_JUMPSPEED:
12106                 default_model_jumpspeed =  GET_FLOAT_ARG(1);
12107                 break;
12108             case CMD_MODELSTXT_JUMPHEIGHT:
12109                 default_model_jumpheight =  GET_FLOAT_ARG(1);
12110                 break;
12111             case CMD_MODELSTXT_GRABDISTANCE:
12112                 default_model_grabdistance =  GET_FLOAT_ARG(1);
12113                 break;
12114             case CMD_MODELSTXT_DEBUG_MNAF:
12115                 move_noatk_factor =  GET_FLOAT_ARG(1);
12116                 break;
12117             case CMD_MODELSTXT_DEBUG_GNAF:
12118                 group_noatk_factor =  GET_FLOAT_ARG(1);
12119                 break;
12120             case CMD_MODELSTXT_DEBUG_ANAF:
12121                 agg_noatk_factor =  GET_FLOAT_ARG(1);
12122                 break;
12123             case CMD_MODELSTXT_DEBUG_MINNA:
12124                 min_noatk_chance =  GET_FLOAT_ARG(1);
12125                 break;
12126             case CMD_MODELSTXT_DEBUG_MAXNA:
12127                 max_noatk_chance =  GET_FLOAT_ARG(1);
12128                 break;
12129             case CMD_MODELSTXT_DEBUG_OSNAF:
12130                 offscreen_noatk_factor =  GET_FLOAT_ARG(1);
12131                 break;
12132             case CMD_MODELSTXT_DEBUG_NAD:
12133                 noatk_duration =  GET_FLOAT_ARG(1);
12134                 break;
12135             default:
12136                 if(cmd >= CMD_MODELSTXT_THE_END)
12137                 {
12138                     printf("command %s not understood in %s, line %d\n", command, filename, line);
12139                 }
12140                 break;
12141             }
12142         }
12143 
12144         // Go to next line
12145         pos += getNewLineStart(buf + pos);
12146     }
12147 
12148     // Defer load_cached_model, so you can define models after their nested model.
12149     printf("\n");
12150 
12151     for(i = 0, pos = 0; i < models_cached; i++)
12152     {
12153         //printf("Checking '%s' '%s'\n", model_cache[i].name, model_cache[i].path);
12154         if(stricmp(model_cache[i].name, "global_model") == 0)
12155         {
12156             global_model = i;
12157         }
12158         if(model_cache[i].loadflag)
12159         {
12160             load_cached_model(model_cache[i].name, "models.txt", 0);
12161             update_loading(&loadingbg[0], ++pos, modelLoadCount);
12162         }
12163     }
12164     printf("\nLoading models...............\tDone!\n");
12165 
12166 
12167     if(buf)
12168     {
12169         free(buf);
12170     }
12171 
12172     return 1;
12173 }
12174 
12175 
12176 
12177 
unload_levelorder()12178 void unload_levelorder()
12179 {
12180     int i, j, t;
12181     s_level_entry *le;
12182     s_set_entry *se;
12183 
12184     if(levelsets)
12185     {
12186         for(i = 0; i < num_difficulties; i++)
12187         {
12188             se = levelsets + i;
12189             if(se->name)
12190             {
12191                 free(se->name);
12192             }
12193             if(se->numlevels)
12194             {
12195                 for(j = 0; j < se->numlevels; j++)
12196                 {
12197                     le = se->levelorder + j;
12198                     if(le->branchname)
12199                     {
12200                         free(le->branchname);
12201                     }
12202                     if(le->filename)
12203                     {
12204                         free(le->filename);
12205                     }
12206                     if(le->skipselect)
12207                     {
12208                         for(t = 0; t < MAX_PLAYERS; t++)
12209                         {
12210                             if(le->skipselect[t])
12211                             {
12212                                 free(le->skipselect[t]);
12213                             }
12214                         }
12215                     }
12216                 }
12217                 free(se->levelorder);
12218             }
12219         }
12220 
12221         free(levelsets);
12222         levelsets = NULL;
12223     }
12224 
12225     num_difficulties = 0;
12226 }
12227 
12228 
12229 
12230 // Add a level to the level order
add_level(char * filename,s_set_entry * set)12231 s_level_entry *add_level(char *filename, s_set_entry *set)
12232 {
12233     s_level_entry *le = NULL;
12234     int Zs[3] = {0, 0, 0};
12235 
12236     if(z_coords[0] > 0)
12237     {
12238         Zs[0] = z_coords[0];
12239     }
12240     else
12241     {
12242         Zs[0] = PLAYER_MIN_Z;
12243     }
12244 
12245     if(z_coords[1] > 0)
12246     {
12247         Zs[1] = z_coords[1];
12248     }
12249     else
12250     {
12251         Zs[1] = PLAYER_MAX_Z;
12252     }
12253 
12254     if(z_coords[2] > 0)
12255     {
12256         Zs[2] = z_coords[2];
12257     }
12258     else
12259     {
12260         Zs[2] = PLAYER_MIN_Z;
12261     }
12262 
12263     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12264     le = set->levelorder + set->numlevels - 1;
12265     memset(le, 0, sizeof(*le));
12266     if(branch_name[0])
12267     {
12268         le->branchname = NAME(branch_name);
12269     }
12270     le->filename = NAME(filename);
12271     le->z_coords[0] = Zs[0];
12272     le->z_coords[1] = Zs[1];
12273     le->z_coords[2] = Zs[2];
12274 
12275     return le;
12276 }
12277 
12278 
12279 
12280 // Add a scene to the level order
add_scene(char * filename,s_set_entry * set)12281 s_level_entry *add_scene(char *filename, s_set_entry *set)
12282 {
12283     s_level_entry *le = NULL;
12284 
12285     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12286     le = set->levelorder + set->numlevels - 1;
12287     memset(le, 0, sizeof(*le));
12288     if(branch_name[0])
12289     {
12290         le->branchname = NAME(branch_name);
12291     }
12292     le->filename = NAME(filename);
12293     le->type = LE_TYPE_CUT_SCENE;
12294     return le;
12295 }
12296 
12297 // Add a select screen file to the level order
add_select(char * filename,s_set_entry * set)12298 s_level_entry *add_select(char *filename, s_set_entry *set)
12299 {
12300     s_level_entry *le = NULL;
12301 
12302     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12303     le = set->levelorder + set->numlevels - 1;
12304     memset(le, 0, sizeof(*le));
12305     if(branch_name[0])
12306     {
12307         le->branchname = NAME(branch_name);
12308     }
12309     le->filename = NAME(filename);
12310     le->type = LE_TYPE_SELECT_SCREEN;
12311     return le;
12312 }
12313 
add_skipselect(ArgList arglist,s_set_entry * set)12314 s_level_entry *add_skipselect(ArgList arglist, s_set_entry *set)
12315 {
12316     s_level_entry *le = NULL;
12317     char *arg;
12318     int i = 0;
12319 
12320     set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder));
12321     le = set->levelorder + set->numlevels - 1;
12322     memset(le, 0, sizeof(*le));
12323     if(branch_name[0])
12324     {
12325         le->branchname = NAME(branch_name);
12326     }
12327 
12328     if(arglist.count == 1)
12329     {
12330         le->noselect = 1;
12331     }
12332     else
12333     {
12334         for(i = 0; i < MAX_PLAYERS; i++)
12335         {
12336             if((arg = GET_ARG(i + 1))[0])
12337             {
12338                 le->skipselect[i] = NAME(arg);
12339             }
12340         }
12341     }
12342 
12343     le->type = LE_TYPE_SKIP_SELECT;
12344     return le;
12345 }
12346 
load_skipselect(char * buf,s_set_entry * set)12347 s_level_entry *load_skipselect(char *buf, s_set_entry *set)
12348 {
12349     ArgList arglist;
12350     char argbuf[MAX_ARG_LEN + 1];
12351 
12352     ParseArgs(&arglist, buf, argbuf);
12353 
12354     return add_skipselect(arglist, set);
12355 }
12356 
save_skipselect(char * buf,char skipselect[MAX_PLAYERS][MAX_NAME_LEN])12357 void save_skipselect(char *buf, char skipselect[MAX_PLAYERS][MAX_NAME_LEN])
12358 {
12359     int i = 0;
12360 
12361     strcpy(buf,"skipselect");
12362 
12363     for (i = 0; i < MAX_PLAYERS; i++)
12364     {
12365         strcat(buf," "); strcat(buf,skipselect[i]);
12366     }
12367 
12368     return;
12369 }
12370 
_readbarstatus(char * buf,s_barstatus * pstatus)12371 static void _readbarstatus(char *buf, s_barstatus *pstatus)
12372 {
12373     char *value;
12374     ArgList arglist;
12375     char argbuf[MAX_ARG_LEN + 1] = "";
12376 
12377     ParseArgs(&arglist, buf, argbuf);
12378     if((value = GET_ARG(1))[0])
12379     {
12380         pstatus->size.x       = atoi(value);
12381     }
12382     else
12383     {
12384         return;
12385     }
12386     if((value = GET_ARG(2))[0])
12387     {
12388         pstatus->size.y       = atoi(value);
12389     }
12390     else
12391     {
12392         return;
12393     }
12394     if((value = GET_ARG(3))[0])
12395     {
12396         pstatus->noborder    = atoi(value);
12397     }
12398     else
12399     {
12400         return;
12401     }
12402     if((value = GET_ARG(4))[0])
12403     {
12404         pstatus->type        = atoi(value);
12405     }
12406     else
12407     {
12408         return;
12409     }
12410     if((value = GET_ARG(5))[0])
12411     {
12412         pstatus->orientation = atoi(value);
12413     }
12414     else
12415     {
12416         return;
12417     }
12418     if((value = GET_ARG(6))[0])
12419     {
12420         pstatus->borderlayer = atoi(value);
12421     }
12422     else
12423     {
12424         return;
12425     }
12426     if((value = GET_ARG(7))[0])
12427     {
12428         pstatus->shadowlayer = atoi(value);
12429     }
12430     else
12431     {
12432         return;
12433     }
12434     if((value = GET_ARG(8))[0])
12435     {
12436         pstatus->barlayer    = atoi(value);
12437     }
12438     else
12439     {
12440         return;
12441     }
12442     if((value = GET_ARG(9))[0])
12443     {
12444         pstatus->backlayer   = atoi(value);
12445     }
12446     else
12447     {
12448         return;
12449     }
12450 }
12451 
add_set()12452 s_set_entry *add_set()
12453 {
12454     s_set_entry *set = NULL;
12455     ++num_difficulties;
12456     if(levelsets)
12457     {
12458         levelsets = realloc(levelsets, sizeof(*levelsets) * num_difficulties);
12459     }
12460     else
12461     {
12462         levelsets = calloc(1, sizeof(*levelsets));
12463     }
12464     set = levelsets + num_difficulties - 1;
12465     memset(set, 0, sizeof(*set));
12466     set->maxplayers = defaultmaxplayers;
12467     return set;
12468 }
12469 
12470 // Load list of levels
load_levelorder()12471 void load_levelorder()
12472 {
12473     static const char *defaulterr = "Error in level order: a set must be specified.";
12474 #define CHKDEF if(!set) { errormessage = (char*) defaulterr; goto lCleanup; }
12475     char filename[128] = "";
12476     int i = 0, j = 0;
12477     char *buf;
12478     size_t size;
12479     int pos;
12480     s_set_entry *set = NULL;
12481     s_level_entry *le = NULL;
12482     char *command;
12483     char *arg;
12484     char *errormessage = NULL;
12485     int plifeUsed[2]  = {0, 0};
12486     int elifeUsed[2]  = {0, 0};
12487     int piconUsed[2]  = {0, 0};
12488     int piconwUsed[2] = {0, 0};
12489     int eiconUsed[4]  = {0, 0, 0, 0};
12490     int pmpUsed[4]    = {0, 0, 0, 0};
12491     int plifeXused[4] = {0, 0, 0, 0};     // 4-7-2006 New custimizable variable for players 'x'
12492     int plifeNused[4] = {0, 0, 0, 0};     // 4-7-2006 New custimizable variable for players 'lives'
12493     int enameused[4]  = {0, 0, 0, 0};     // 4-7-2006 New custimizable variable for enemy names
12494     int pnameJused[4] = {0, 0, 0, 0};     // 1-8-2006 New custimizable variable for players name Select Hero
12495     int pscoreUsed[4] = {0, 0, 0, 0};     // 1-8-2006 New custimizable variable for players name Select Hero
12496 
12497     ArgList arglist;
12498     char argbuf[MAX_ARG_LEN + 1] = "";
12499     levelOrderCommands cmd;
12500     int line = 0;
12501 
12502     unload_levelorder();
12503 
12504     if(custLevels != NULL)
12505     {
12506         strcpy(filename, "data/");
12507         strcat(filename, custLevels);
12508     }
12509     else
12510     {
12511         strcpy(filename, "data/levels.txt");
12512     }
12513 
12514     // Read file
12515 
12516     if(buffer_pakfile(filename, &buf, &size) != 1)
12517     {
12518         shutdown(1, "Error loading level list from %s", filename);
12519     }
12520 
12521     // Now interpret the contents of buf line by line
12522     pos = 0;
12523 
12524     // Custom lifebar/timebox/icon positioning and size
12525     picon[0][0] = piconw[0][0] = picon[2][0] = piconw[2][0] = eicon[0][0] = eicon[2][0] = 2;
12526     picon[1][0] = piconw[1][0] = picon[3][0] = piconw[3][0] = eicon[1][0] = eicon[3][0] = 2 + P2_STATS_DIST;
12527     picon[0][1] = piconw[0][1] = picon[1][1] = piconw[1][1] = 2;
12528     picon[2][1] = piconw[2][1] = picon[3][1] = piconw[3][1] = 202;
12529     plife[0][0] = pmp[0][0] = plife[2][0] = pmp[2][0] = elife[0][0] = elife[2][0] = 20;
12530     plife[1][0] = pmp[1][0] = plife[3][0] = pmp[3][0] = elife[1][0] = elife[3][0] = 20 + P2_STATS_DIST;
12531     plife[0][1] = plife[1][1] = 10;
12532     plife[2][1] = plife[3][1] = 210;
12533     pmp[0][1] = pmp[1][1] = 18;
12534     pmp[2][1] = pmp[3][1] = 218;
12535 
12536     memset(psmenu, 0, sizeof(psmenu));
12537 
12538     eicon[0][1] = eicon[1][1] = 19;
12539     eicon[2][1] = eicon[3][1] = 220;
12540     elife[0][1] = elife[1][1] = 27;
12541     elife[2][1] = elife[3][1] = 227;
12542 
12543     timeloc[0] = 149;
12544     timeloc[1] = 4;
12545     timeloc[2] = 21;
12546     timeloc[3] = 20;
12547     timeloc[4] = 0;
12548 
12549     lbarstatus.size.x  = mpbarstatus.size.x = 100;
12550     lbarstatus.size.y  = 5;
12551     mpbarstatus.size.y = 3;
12552     lbarstatus.noborder = mpbarstatus.noborder = 0;
12553 
12554     // Show Complete Default Values
12555     scomplete[0] = 75;
12556     scomplete[1] = 60;
12557     scomplete[2] = 0;
12558     scomplete[3] = 0;
12559     scomplete[4] = 0;
12560     scomplete[5] = 0;
12561 
12562     // Show Complete Y Values
12563     cbonus[0] = lbonus[0] = rbonus[0] = tscore[0] = 10;
12564     cbonus[1] = cbonus[3] = cbonus[5] = cbonus[7] = cbonus[9] = 100;
12565     lbonus[1] = lbonus[3] = lbonus[5] = lbonus[7] = lbonus[9] = 120;
12566     rbonus[1] = rbonus[3] = rbonus[5] = rbonus[7] = rbonus[9] = 140;
12567     tscore[1] = tscore[3] = tscore[5] = tscore[7] = tscore[9] = 160;
12568 
12569     // Show Complete X Values
12570     cbonus[2] = lbonus[2] = rbonus[2] = tscore[2] = 100;
12571     cbonus[4] = lbonus[4] = rbonus[4] = tscore[4] = 155;
12572     cbonus[6] = lbonus[6] = rbonus[6] = tscore[6] = 210;
12573     cbonus[8] = lbonus[8] = rbonus[8] = tscore[8] = 265;
12574 
12575     while(pos < size)
12576     {
12577         line++;
12578         ParseArgs(&arglist, buf + pos, argbuf);
12579         command = GET_ARG(0);
12580         cmd = getLevelOrderCommand(levelordercmdlist, command);
12581         switch(cmd)
12582         {
12583         case CMD_LEVELORDER_BLENDFX:
12584             for(i = 0; i < MAX_BLENDINGS; i++)
12585             {
12586                 if(GET_INT_ARG(i + 1))
12587                 {
12588                     blendfx[i] = 1;
12589                 }
12590                 else
12591                 {
12592                     blendfx[i] = 0;
12593                 }
12594             }
12595             blendfx_is_set = 1;
12596             break;
12597         case CMD_LEVELORDER_SET:
12598             set = add_set();
12599             set->name = NAME(GET_ARG(1));
12600             set->ifcomplete = 0;
12601             set->saveflag  = 1; // default to 1, so the level can be saved
12602             branch_name[0] = 0;
12603             le = NULL;
12604             break;
12605         case CMD_LEVELORDER_IFCOMPLETE:
12606             CHKDEF;
12607             set->ifcomplete = GET_INT_ARG(1);
12608             break;
12609         case CMD_LEVELORDER_SKIPSELECT:
12610             CHKDEF;
12611             le = add_skipselect(arglist, set);
12612             break;
12613         case CMD_LEVELORDER_FILE:
12614             CHKDEF;
12615             le = add_level(GET_ARG(1), set);
12616             break;
12617         case CMD_LEVELORDER_SCENE:
12618             CHKDEF;
12619             le = add_scene(GET_ARG(1), set);
12620             break;
12621         case CMD_LEVELORDER_SELECT:
12622             CHKDEF;
12623             le = add_select(GET_ARG(1), set);
12624             break;
12625         case CMD_LEVELORDER_NEXT:
12626             CHKDEF;
12627             // Set 'gonext' flag of last loaded level
12628             if(le)
12629             {
12630                 le->gonext = 1;
12631             }
12632             break;
12633         case CMD_LEVELORDER_END:
12634             CHKDEF;
12635             // Set endgame flag of last loaded level
12636             if(le)
12637             {
12638                 le->gonext = 2;
12639             }
12640             break;
12641         case CMD_LEVELORDER_LIVES:
12642             // 7-1-2005  credits/lives/singleplayer start here
12643             // used to read the new # of lives/credits from the levels.txt
12644             CHKDEF;
12645             set->lives = GET_INT_ARG(1);
12646             break;
12647         case CMD_LEVELORDER_DISABLEHOF:
12648             CHKDEF;
12649             set->noshowhof = GET_INT_ARG(1);
12650             break;
12651         case CMD_LEVELORDER_DISABLEGAMEOVER:
12652             CHKDEF;
12653             set->noshowgameover = GET_INT_ARG(1);
12654             break;
12655         case CMD_LEVELORDER_CANSAVE:
12656             // 07-12-31
12657             // 0 this set can't be saved
12658             // 1 save level only
12659             // 2 save player info and level, can't choose player in select menu
12660             CHKDEF;
12661             set->saveflag = GET_INT_ARG(1);
12662             break;
12663         case CMD_LEVELORDER_Z:
12664             //    2-10-05  adjust the walkable coordinates
12665             CHKDEF;
12666             z_coords[0] = GET_INT_ARG(1);
12667             z_coords[1] = GET_INT_ARG(2);
12668             z_coords[2] = GET_INT_ARG(3);
12669             break;
12670         case CMD_LEVELORDER_BRANCH:
12671             //    2007-2-22 level branch name
12672             CHKDEF;
12673             strncpy(branch_name, GET_ARG(1), MAX_NAME_LEN);
12674             break;
12675         case CMD_LEVELORDER_P1LIFE:
12676         case CMD_LEVELORDER_P2LIFE:
12677         case CMD_LEVELORDER_P3LIFE:
12678         case CMD_LEVELORDER_P4LIFE:
12679             switch(cmd)
12680             {
12681             case CMD_LEVELORDER_P1LIFE:
12682                 i = 0;
12683                 break;
12684             case CMD_LEVELORDER_P2LIFE:
12685                 i = 1;
12686                 break;
12687             case CMD_LEVELORDER_P3LIFE:
12688                 i = 2;
12689                 plifeUsed[0] = 1;
12690                 break;
12691             case CMD_LEVELORDER_P4LIFE:
12692                 i = 3;
12693                 plifeUsed[1] = 1;
12694                 break;
12695             default:
12696                 assert(0);
12697             }
12698             if((arg = GET_ARG(1))[0])
12699             {
12700                 plife[i][0] = atoi(arg);
12701             }
12702             if((arg = GET_ARG(2))[0])
12703             {
12704                 plife[i][1] = atoi(arg);
12705             }
12706             break;
12707         case CMD_LEVELORDER_P1MP:
12708         case CMD_LEVELORDER_P2MP:
12709         case CMD_LEVELORDER_P3MP:
12710         case CMD_LEVELORDER_P4MP:
12711             switch(cmd)
12712             {
12713             case CMD_LEVELORDER_P1MP:
12714                 i = 0;
12715                 break;
12716             case CMD_LEVELORDER_P2MP:
12717                 i = 1;
12718                 break;
12719             case CMD_LEVELORDER_P3MP:
12720                 i = 2;
12721                 break;
12722             case CMD_LEVELORDER_P4MP:
12723                 i = 3;
12724                 break;
12725             default:
12726                 assert(0);
12727             }
12728             if((arg = GET_ARG(1))[0])
12729             {
12730                 pmp[i][0] = atoi(arg);
12731             }
12732             if((arg = GET_ARG(2))[0])
12733             {
12734                 pmp[i][1] = atoi(arg);
12735             }
12736             pmpUsed[i] = 1;
12737             break;
12738         case CMD_LEVELORDER_P1LIFEX:
12739         case CMD_LEVELORDER_P2LIFEX:
12740         case CMD_LEVELORDER_P3LIFEX:
12741         case CMD_LEVELORDER_P4LIFEX:
12742             switch(cmd)
12743             {
12744             case CMD_LEVELORDER_P1LIFEX:
12745                 j = 0;
12746                 break;
12747             case CMD_LEVELORDER_P2LIFEX:
12748                 j = 1;
12749                 break;
12750             case CMD_LEVELORDER_P3LIFEX:
12751                 j = 2;
12752                 break;
12753             case CMD_LEVELORDER_P4LIFEX:
12754                 j = 3;
12755                 break;
12756             default:
12757                 assert(0);
12758             }
12759             for(i = 0; i < 3; i++)
12760                 if((arg = GET_ARG(i + 1))[0])
12761                 {
12762                     plifeX[j][i] = atoi(arg);
12763                 }
12764             plifeXused[j] = 1;
12765             break;
12766         case CMD_LEVELORDER_P1LIFEN:
12767         case CMD_LEVELORDER_P2LIFEN:
12768         case CMD_LEVELORDER_P3LIFEN:
12769         case CMD_LEVELORDER_P4LIFEN:
12770             switch(cmd)
12771             {
12772             case CMD_LEVELORDER_P1LIFEN:
12773                 j = 0;
12774                 break;
12775             case CMD_LEVELORDER_P2LIFEN:
12776                 j = 1;
12777                 break;
12778             case CMD_LEVELORDER_P3LIFEN:
12779                 j = 2;
12780                 break;
12781             case CMD_LEVELORDER_P4LIFEN:
12782                 j = 3;
12783                 break;
12784             default:
12785                 assert(0);
12786             }
12787             for(i = 0; i < 3; i++)
12788                 if((arg = GET_ARG(i + 1))[0])
12789                 {
12790                     plifeN[j][i] = atoi(arg);
12791                 }
12792             plifeNused[j] = 1;
12793             break;
12794         case CMD_LEVELORDER_E1LIFE:
12795         case CMD_LEVELORDER_E2LIFE:
12796         case CMD_LEVELORDER_E3LIFE:
12797         case CMD_LEVELORDER_E4LIFE:
12798             switch(cmd)
12799             {
12800             case CMD_LEVELORDER_E1LIFE:
12801                 i = 0;
12802                 break;
12803             case CMD_LEVELORDER_E2LIFE:
12804                 i = 1;
12805                 break;
12806             case CMD_LEVELORDER_E3LIFE:
12807                 i = 2;
12808                 elifeUsed[0] = 1;
12809                 break;
12810             case CMD_LEVELORDER_E4LIFE:
12811                 i = 3;
12812                 elifeUsed[1] = 1;
12813                 break;
12814             default:
12815                 assert(0);
12816             }
12817             if((arg = GET_ARG(1))[0])
12818             {
12819                 elife[i][0] = atoi(arg);
12820             }
12821             if((arg = GET_ARG(2))[0])
12822             {
12823                 elife[i][1] = atoi(arg);
12824             }
12825             break;
12826         case CMD_LEVELORDER_P1ICON:
12827         case CMD_LEVELORDER_P2ICON:
12828         case CMD_LEVELORDER_P3ICON:
12829         case CMD_LEVELORDER_P4ICON:
12830             switch(cmd)
12831             {
12832             case CMD_LEVELORDER_P1ICON:
12833                 i = 0;
12834                 break;
12835             case CMD_LEVELORDER_P2ICON:
12836                 i = 1;
12837                 break;
12838             case CMD_LEVELORDER_P3ICON:
12839                 i = 2;
12840                 piconUsed[0] = 1;
12841                 break;
12842             case CMD_LEVELORDER_P4ICON:
12843                 i = 3;
12844                 piconUsed[1] = 1;
12845                 break;
12846             default:
12847                 assert(0);
12848             }
12849             if((arg = GET_ARG(1))[0])
12850             {
12851                 picon[i][0] = atoi(arg);
12852             }
12853             if((arg = GET_ARG(2))[0])
12854             {
12855                 picon[i][1] = atoi(arg);
12856             }
12857             break;
12858         case CMD_LEVELORDER_P1ICONW:
12859         case CMD_LEVELORDER_P2ICONW:
12860         case CMD_LEVELORDER_P3ICONW:
12861         case CMD_LEVELORDER_P4ICONW:
12862             switch(cmd)
12863             {
12864             case CMD_LEVELORDER_P1ICONW:
12865                 i = 0;
12866                 break;
12867             case CMD_LEVELORDER_P2ICONW:
12868                 i = 1;
12869                 break;
12870             case CMD_LEVELORDER_P3ICONW:
12871                 i = 2;
12872                 piconwUsed[0] = 1;
12873                 break;
12874             case CMD_LEVELORDER_P4ICONW:
12875                 i = 3;
12876                 piconwUsed[1] = 1;
12877                 break;
12878             default:
12879                 assert(0);
12880             }
12881             if((arg = GET_ARG(1))[0])
12882             {
12883                 piconw[i][0] = atoi(arg);
12884             }
12885             if((arg = GET_ARG(2))[0])
12886             {
12887                 piconw[i][1] = atoi(arg);
12888             }
12889             break;
12890         case CMD_LEVELORDER_MP1ICON:
12891         case CMD_LEVELORDER_MP2ICON:
12892         case CMD_LEVELORDER_MP3ICON:
12893         case CMD_LEVELORDER_MP4ICON:
12894             switch(cmd)
12895             {
12896             case CMD_LEVELORDER_MP1ICON:
12897                 i = 0;
12898                 break;
12899             case CMD_LEVELORDER_MP2ICON:
12900                 i = 1;
12901                 break;
12902             case CMD_LEVELORDER_MP3ICON:
12903                 i = 2;
12904                 break;
12905             case CMD_LEVELORDER_MP4ICON:
12906                 i = 3;
12907                 break;
12908             default:
12909                 assert(0);
12910             }
12911             if((arg = GET_ARG(1))[0])
12912             {
12913                 mpicon[i][0] = atoi(arg);
12914             }
12915             if((arg = GET_ARG(2))[0])
12916             {
12917                 mpicon[i][1] = atoi(arg);
12918             }
12919             break;
12920         case CMD_LEVELORDER_P1NAMEJ:
12921         case CMD_LEVELORDER_P2NAMEJ:
12922         case CMD_LEVELORDER_P3NAMEJ:
12923         case CMD_LEVELORDER_P4NAMEJ:
12924             switch(cmd)
12925             {
12926             case CMD_LEVELORDER_P1NAMEJ:
12927                 j = 0;
12928                 break;
12929             case CMD_LEVELORDER_P2NAMEJ:
12930                 j = 1;
12931                 break;
12932             case CMD_LEVELORDER_P3NAMEJ:
12933                 j = 2;
12934                 break;
12935             case CMD_LEVELORDER_P4NAMEJ:
12936                 j = 3;
12937                 break;
12938             default:
12939                 assert(0);
12940             }
12941             for(i = 0; i < 7; i++)
12942                 if((arg = GET_ARG(i + 1))[0])
12943                 {
12944                     pnameJ[j][i] = atoi(arg);
12945                 }
12946             pnameJused[j] = 1;
12947             break;
12948         case CMD_LEVELORDER_P1SCORE:
12949         case CMD_LEVELORDER_P2SCORE:
12950         case CMD_LEVELORDER_P3SCORE:
12951         case CMD_LEVELORDER_P4SCORE:
12952             switch(cmd)
12953             {
12954             case CMD_LEVELORDER_P1SCORE:
12955                 j = 0;
12956                 break;
12957             case CMD_LEVELORDER_P2SCORE:
12958                 j = 1;
12959                 break;
12960             case CMD_LEVELORDER_P3SCORE:
12961                 j = 2;
12962                 break;
12963             case CMD_LEVELORDER_P4SCORE:
12964                 j = 3;
12965                 break;
12966             default:
12967                 assert(0);
12968             }
12969             for(i = 0; i < 7; i++)
12970                 if((arg = GET_ARG(i + 1))[0])
12971                 {
12972                     pscore[j][i] = atoi(arg);
12973                 }
12974             pscoreUsed[j] = 1;
12975             break;
12976         case CMD_LEVELORDER_P1SHOOT:
12977         case CMD_LEVELORDER_P2SHOOT:
12978         case CMD_LEVELORDER_P3SHOOT:
12979         case CMD_LEVELORDER_P4SHOOT:
12980             switch(cmd)
12981             {
12982             case CMD_LEVELORDER_P1SHOOT:
12983                 j = 0;
12984                 break;
12985             case CMD_LEVELORDER_P2SHOOT:
12986                 j = 1;
12987                 break;
12988             case CMD_LEVELORDER_P3SHOOT:
12989                 j = 2;
12990                 break;
12991             case CMD_LEVELORDER_P4SHOOT:
12992                 j = 3;
12993                 break;
12994             default:
12995                 assert(0);
12996             }
12997             for(i = 0; i < 3; i++)
12998                 if((arg = GET_ARG(i + 1))[0])
12999                 {
13000                     pshoot[j][i] = atoi(arg);
13001                 }
13002             break;
13003         case CMD_LEVELORDER_P1RUSH:
13004         case CMD_LEVELORDER_P2RUSH:
13005         case CMD_LEVELORDER_P3RUSH:
13006         case CMD_LEVELORDER_P4RUSH:
13007             switch(cmd)
13008             {
13009             case CMD_LEVELORDER_P1RUSH:
13010                 j = 0;
13011                 break;
13012             case CMD_LEVELORDER_P2RUSH:
13013                 j = 1;
13014                 break;
13015             case CMD_LEVELORDER_P3RUSH:
13016                 j = 2;
13017                 break;
13018             case CMD_LEVELORDER_P4RUSH:
13019                 j = 3;
13020                 break;
13021             default:
13022                 assert(0);
13023             }
13024             for(i = 0; i < 8; i++)
13025                 if((arg = GET_ARG(i + 1))[0])
13026                 {
13027                     prush[j][i] = atoi(arg);
13028                 }
13029             break;
13030         case CMD_LEVELORDER_E1ICON:
13031         case CMD_LEVELORDER_E2ICON:
13032         case CMD_LEVELORDER_E3ICON:
13033         case CMD_LEVELORDER_E4ICON:
13034             switch(cmd)
13035             {
13036             case CMD_LEVELORDER_E1ICON:
13037                 i = 0;
13038                 break;
13039             case CMD_LEVELORDER_E2ICON:
13040                 i = 1;
13041                 break;
13042             case CMD_LEVELORDER_E3ICON:
13043                 i = 2;
13044                 eiconUsed[0] = 1;
13045                 break;
13046             case CMD_LEVELORDER_E4ICON:
13047                 i = 3;
13048                 eiconUsed[1] = 1;
13049                 break;
13050             default:
13051                 assert(0);
13052             }
13053             if((arg = GET_ARG(1))[0])
13054             {
13055                 eicon[i][0] = atoi(arg);
13056             }
13057             if((arg = GET_ARG(2))[0])
13058             {
13059                 eicon[i][1] = atoi(arg);
13060             }
13061             break;
13062         case CMD_LEVELORDER_E1NAME:
13063         case CMD_LEVELORDER_E2NAME:
13064         case CMD_LEVELORDER_E3NAME:
13065         case CMD_LEVELORDER_E4NAME:
13066             switch(cmd)
13067             {
13068             case CMD_LEVELORDER_E1NAME:
13069                 j = 0;
13070                 break;
13071             case CMD_LEVELORDER_E2NAME:
13072                 j = 1;
13073                 break;
13074             case CMD_LEVELORDER_E3NAME:
13075                 j = 2;
13076                 break;
13077             case CMD_LEVELORDER_E4NAME:
13078                 j = 3;
13079                 break;
13080             default:
13081                 assert(0);
13082             }
13083             for(i = 0; i < 3; i++)
13084                 if((arg = GET_ARG(i + 1))[0])
13085                 {
13086                     ename[j][i] = atoi(arg);
13087                 }
13088             enameused[j] = 1;
13089             break;
13090         case CMD_LEVELORDER_P1SMENU:
13091         case CMD_LEVELORDER_P2SMENU:
13092         case CMD_LEVELORDER_P3SMENU:
13093         case CMD_LEVELORDER_P4SMENU:
13094             switch(cmd)
13095             {
13096             case CMD_LEVELORDER_P1SMENU:
13097                 j = 0;
13098                 break;
13099             case CMD_LEVELORDER_P2SMENU:
13100                 j = 1;
13101                 break;
13102             case CMD_LEVELORDER_P3SMENU:
13103                 j = 2;
13104                 break;
13105             case CMD_LEVELORDER_P4SMENU:
13106                 j = 3;
13107                 break;
13108             default:
13109                 assert(0);
13110             }
13111             for(i = 0; i < 4; i++)
13112                 if((arg = GET_ARG(i + 1))[0])
13113                 {
13114                     psmenu[j][i] = atoi(arg);
13115                 }
13116             break;
13117         case CMD_LEVELORDER_TIMEICON:
13118             strncpy(timeicon_path, GET_ARG(1), 127);
13119             timeicon = loadsprite(timeicon_path, 0, 0, pixelformat);
13120             if((arg = GET_ARG(2))[0])
13121             {
13122                 timeicon_offsets[0] = atoi(arg);
13123             }
13124             if((arg = GET_ARG(3))[0])
13125             {
13126                 timeicon_offsets[1] = atoi(arg);
13127             }
13128             break;
13129         case CMD_LEVELORDER_BGICON:
13130             strncpy(bgicon_path, GET_ARG(1), 127);
13131             bgicon = loadsprite(bgicon_path, 0, 0, pixelformat);
13132             if((arg = GET_ARG(2))[0])
13133             {
13134                 bgicon_offsets[0] = atoi(arg);
13135             }
13136             if((arg = GET_ARG(3))[0])
13137             {
13138                 bgicon_offsets[1] = atoi(arg);
13139             }
13140             if((arg = GET_ARG(4))[0])
13141             {
13142                 bgicon_offsets[2] = atoi(arg);
13143             }
13144             else
13145             {
13146                 bgicon_offsets[2] = HUD_Z / 2;
13147             }
13148             break;
13149         case CMD_LEVELORDER_OLICON:
13150             strncpy(olicon_path, GET_ARG(1), 127);
13151             olicon = loadsprite(olicon_path, 0, 0, pixelformat);
13152             if((arg = GET_ARG(2))[0])
13153             {
13154                 olicon_offsets[0] = atoi(arg);
13155             }
13156             if((arg = GET_ARG(3))[0])
13157             {
13158                 olicon_offsets[1] = atoi(arg);
13159             }
13160             if((arg = GET_ARG(4))[0])
13161             {
13162                 olicon_offsets[2] = atoi(arg);
13163             }
13164             else
13165             {
13166                 olicon_offsets[2] = HUD_Z * 3;
13167             }
13168             break;
13169         case CMD_LEVELORDER_TIMELOC:
13170             for(i = 0; i < 6; i++)
13171                 if((arg = GET_ARG(i + 1))[0])
13172                 {
13173                     timeloc[i] = atoi(arg);
13174                 }
13175             break;
13176         case CMD_LEVELORDER_PAUSEOFFSET:
13177             for(i = 0; i < 7; i++)
13178                 if((arg = GET_ARG(i + 1))[0])
13179                 {
13180                     pauseoffset[i] = atoi(arg);
13181                 }
13182             break;
13183         case CMD_LEVELORDER_LBARSIZE:
13184             _readbarstatus(buf + pos, &lbarstatus);
13185             break;
13186         case CMD_LEVELORDER_OLBARSIZE:
13187             _readbarstatus(buf + pos, &olbarstatus);
13188             break;
13189         case CMD_LEVELORDER_MPBARSIZE:
13190             _readbarstatus(buf + pos, &mpbarstatus);
13191             break;
13192         case CMD_LEVELORDER_LBARTEXT:
13193             for(i = 0; i < 4; i++)
13194                 if((arg = GET_ARG(i + 1))[0])
13195                 {
13196                     lbartext[i] = atoi(arg);
13197                 }
13198             break;
13199         case CMD_LEVELORDER_MPBARTEXT:
13200             for(i = 0; i < 4; i++)
13201                 if((arg = GET_ARG(i + 1))[0])
13202                 {
13203                     mpbartext[i] = atoi(arg);
13204                 }
13205             break;
13206         case CMD_LEVELORDER_SHOWCOMPLETE:
13207             for(i = 0; i < 6; i++)
13208                 if((arg = GET_ARG(i + 1))[0])
13209                 {
13210                     scomplete[i] = atoi(arg);
13211                 }
13212             break;
13213         case CMD_LEVELORDER_CLEARBONUS:
13214             for(i = 0; i < 10; i++)
13215                 if((arg = GET_ARG(i + 1))[0])
13216                 {
13217                     cbonus[i] = atoi(arg);
13218                 }
13219             break;
13220         case CMD_LEVELORDER_RUSHBONUS:
13221             for(i = 0; i < 10; i++)
13222                 if((arg = GET_ARG(i + 1))[0])
13223                 {
13224                     rbonus[i] = atoi(arg);
13225                 }
13226             break;
13227         case CMD_LEVELORDER_LIFEBONUS:
13228             for(i = 0; i < 10; i++)
13229                 if((arg = GET_ARG(i + 1))[0])
13230                 {
13231                     lbonus[i] = atoi(arg);
13232                 }
13233             break;
13234         case CMD_LEVELORDER_SCBONUSES:
13235             for(i = 0; i < 4; i++)
13236                 if((arg = GET_ARG(i + 1))[0])
13237                 {
13238                     scbonuses[i] = atoi(arg);
13239                 }
13240             break;
13241         case CMD_LEVELORDER_TOTALSCORE:
13242             for(i = 0; i < 10; i++)
13243                 if((arg = GET_ARG(i + 1))[0])
13244                 {
13245                     tscore[i] = atoi(arg);
13246                 }
13247             break;
13248         case CMD_LEVELORDER_MUSICOVERLAP:
13249             CHKDEF;
13250             set->musicoverlap = GET_INT_ARG(1);
13251             break;
13252         case CMD_LEVELORDER_SHOWRUSHBONUS:
13253             showrushbonus = 1;
13254             break;
13255         case CMD_LEVELORDER_NOSLOWFX:
13256             noslowfx = 1;
13257             break;
13258         case CMD_LEVELORDER_EQUALAIRPAUSE:
13259             equalairpause = 1;
13260             break;
13261         case CMD_LEVELORDER_HISCOREBG:
13262             hiscorebg = 1;
13263             break;
13264         case CMD_LEVELORDER_COMPLETEBG:
13265             completebg = 1;
13266             break;
13267         case CMD_LEVELORDER_LOADINGBG:
13268             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));
13269             if(errormessage)
13270             {
13271                 goto lCleanup;
13272             }
13273             break;
13274         case CMD_LEVELORDER_LOADINGBG2:
13275             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));
13276             if(errormessage)
13277             {
13278                 goto lCleanup;
13279             }
13280             break;
13281         case CMD_LEVELORDER_LOADINGMUSIC:
13282             loadingmusic = GET_INT_ARG(1);
13283             break;
13284         case CMD_LEVELORDER_UNLOCKBG:
13285             unlockbg = 1;
13286             break;
13287         case CMD_LEVELORDER_NOSHARE:
13288             noshare = 1;
13289             break;
13290         case CMD_LEVELORDER_CUSTFADE:
13291             //8-2-2005 custom fade
13292             CHKDEF;
13293             set->custfade = GET_INT_ARG(1);
13294             break;
13295         case CMD_LEVELORDER_CONTINUESCORE:
13296             //8-2-2005 custom fade end
13297             //continuescore
13298             CHKDEF;
13299             set->continuescore = GET_INT_ARG(1);
13300             break;
13301         case CMD_LEVELORDER_CREDITS:
13302             CHKDEF;
13303             set->credits = GET_INT_ARG(1);
13304             break;
13305         case CMD_LEVELORDER_TYPEMP:
13306             //typemp for change for mp restored by time (0) to by enemys (1) or no restore (2) by tails
13307             CHKDEF;
13308             set->typemp = GET_INT_ARG(1);
13309             break;
13310         case CMD_LEVELORDER_SINGLE:
13311             if(set)
13312             {
13313                 set->maxplayers = 1;
13314             }
13315             else
13316             {
13317                 defaultmaxplayers = 1;
13318             }
13319             break;
13320         case CMD_LEVELORDER_MAXPLAYERS:
13321             if(set)
13322             {
13323                 set->maxplayers = GET_INT_ARG(1);
13324             }
13325             else
13326             {
13327                 defaultmaxplayers = GET_INT_ARG(1);
13328             }
13329             break;
13330         case CMD_LEVELORDER_NOSAME:
13331             CHKDEF;
13332             set->nosame |= GET_INT_ARG(1) != 0;
13333             set->nosame |= GET_INT_ARG(2) != 0 ? 2 : 0;
13334             break;
13335         case CMD_LEVELORDER_RUSH:
13336             rush[0] = GET_INT_ARG(1);
13337             rush[1] = GET_INT_ARG(2);
13338             strncpy(rush_names[0], GET_ARG(3), MAX_NAME_LEN);
13339             rush[2] = GET_INT_ARG(4);
13340             rush[3] = GET_INT_ARG(5);
13341             strncpy(rush_names[1], GET_ARG(6), MAX_NAME_LEN);
13342             rush[4] = GET_INT_ARG(7);
13343             rush[5] = GET_INT_ARG(8);
13344             break;
13345         case CMD_LEVELORDER_MAXWALLHEIGHT:
13346             MAX_WALL_HEIGHT = GET_INT_ARG(1);
13347             if(MAX_WALL_HEIGHT < 0)
13348             {
13349                 MAX_WALL_HEIGHT = 1000;
13350             }
13351             break;
13352         case CMD_LEVELORDER_SCOREFORMAT:
13353             scoreformat = GET_INT_ARG(1);
13354             break;
13355         case CMD_LEVELORDER_GRAVITY:
13356             default_level_gravity = GET_FLOAT_ARG(1);
13357             default_level_maxfallspeed = GET_FLOAT_ARG(2);
13358             default_level_maxtossspeed = GET_FLOAT_ARG(3);
13359             break;
13360         case CMD_LEVELORDER_SKIPTOSET:
13361             skiptoset = GET_INT_ARG(1);
13362             break;
13363         case CMD_LEVELORDER_NOSHOWCOMPLETE:
13364             CHKDEF;
13365             set->noshowcomplete = GET_INT_ARG(1);
13366             break;
13367         case CMD_LEVELORDER_SPAWNOVERRIDE:
13368             spawnoverride = GET_INT_ARG(1);
13369             break;
13370         case CMD_LEVELORDER_MAXENTITIES:
13371             maxentities = GET_INT_ARG(1);
13372             break;
13373         default:
13374             if (command && command[0])
13375             {
13376                 printf("Command '%s' not understood in level order!", command);
13377             }
13378         }
13379 
13380         // Go to next line
13381         pos += getNewLineStart(buf + pos);
13382     }
13383 
13384 #undef CHKDEF
13385 
13386     // Variables without defaults will be auto populated.
13387     if(olbarstatus.size.x == 0)
13388     {
13389         olbarstatus = lbarstatus;
13390     }
13391 
13392     if(!plifeUsed[0])
13393     {
13394         plife[2][0] = plife[0][0];
13395         plife[2][1] = plife[2][1] + (plife[0][1] - 10);
13396     }
13397     if(!plifeUsed[1])
13398     {
13399         plife[3][0] = plife[1][0];
13400         plife[3][1] = plife[3][1] + (plife[1][1] - 10);
13401     }
13402 
13403     if(!elifeUsed[0])
13404     {
13405         elife[2][0] = elife[0][0];
13406         elife[2][1] = elife[2][1] + (elife[0][1] - 27);
13407     }
13408     if(!elifeUsed[1])
13409     {
13410         elife[3][0] = elife[1][0];
13411         elife[3][1] = elife[3][1] + (elife[1][1] - 27);
13412     }
13413 
13414     if(!piconUsed[0])
13415     {
13416         picon[2][0] = picon[0][0];
13417         picon[2][1] = picon[2][1] + (picon[0][1] - 2);
13418     }
13419     if(!piconUsed[1])
13420     {
13421         picon[3][0] = picon[1][0];
13422         picon[3][1] = picon[3][1] + (picon[1][1] - 2);
13423     }
13424 
13425     if(!piconwUsed[0])
13426     {
13427         piconw[2][0] = piconw[0][0];
13428         piconw[2][1] = piconw[2][1] + (piconw[0][1] - 2);
13429     }
13430     if(!piconwUsed[1])
13431     {
13432         piconw[3][0] = piconw[1][0];
13433         piconw[3][1] = piconw[3][1] + (piconw[1][1] - 2);
13434     }
13435 
13436     if(!eiconUsed[0])
13437     {
13438         eicon[2][0] = eicon[0][0];
13439         eicon[2][1] = eicon[2][1] + (eicon[0][1] - 19);
13440     }
13441     if(!eiconUsed[1])
13442     {
13443         eicon[3][0] = eicon[1][0];
13444         eicon[3][1] = eicon[3][1] + (eicon[1][1] - 19);
13445     }
13446 
13447     if(!pmpUsed[0])
13448     {
13449         pmp[0][0] = plife[0][0];
13450         pmp[0][1] = plife[0][1] + 8;
13451     }
13452     if(!pmpUsed[1])
13453     {
13454         pmp[1][0] = plife[1][0];
13455         pmp[1][1] = plife[1][1] + 8;
13456     }
13457     if(!pmpUsed[2])
13458     {
13459         pmp[2][0] = pmp[0][0];
13460         pmp[2][1] = pmp[2][1] + (pmp[0][1] - 18);
13461     }
13462     if(!pmpUsed[3])
13463     {
13464         pmp[3][0] = pmp[1][0];
13465         pmp[3][1] = pmp[1][1] + (pmp[1][1] - 18);
13466     }
13467 
13468     if(!plifeXused[0])
13469     {
13470         plifeX[0][0] = plife[0][0] + lbarstatus.size.x + 4;
13471         plifeX[0][1] = picon[0][1] + 7;
13472     }
13473     if(!plifeXused[1])
13474     {
13475         plifeX[1][0] = plife[1][0] + lbarstatus.size.x + 4;
13476         plifeX[1][1] = picon[1][1] + 7;
13477     }
13478     if(!plifeXused[2])
13479     {
13480         plifeX[2][0] = plife[2][0] + lbarstatus.size.x + 4;
13481         plifeX[2][1] = picon[2][1] + 7;
13482     }
13483     if(!plifeXused[3])
13484     {
13485         plifeX[3][0] = plife[3][0] + lbarstatus.size.x + 4;
13486         plifeX[3][1] = picon[3][1] + 7;
13487     }
13488     for(i = 0; i < 4; i++) if(plifeX[i][2] == -1)
13489         {
13490             plifeX[i][2] = 0;
13491         }
13492 
13493     if(!plifeNused[0])
13494     {
13495         plifeN[0][0] = plife[0][0] + lbarstatus.size.x + 11;
13496         plifeN[0][1] = picon[0][1];
13497     }
13498     if(!plifeNused[1])
13499     {
13500         plifeN[1][0] = plife[1][0] + lbarstatus.size.x + 11;
13501         plifeN[1][1] = picon[1][1];
13502     }
13503     if(!plifeNused[2])
13504     {
13505         plifeN[2][0] = plifeN[0][0];
13506         plifeN[2][1] = picon[2][1];
13507     }
13508     if(!plifeNused[3])
13509     {
13510         plifeN[3][0] = plifeN[1][0];
13511         plifeN[3][1] = picon[3][1];
13512     }
13513     for(i = 0; i < 4; i++) if(plifeN[i][2] == -1)
13514         {
13515             plifeN[i][2] = 3;
13516         }
13517 
13518     if(!pnameJused[0])
13519     {
13520         pnameJ[0][2] = pnameJ[0][4] = pnameJ[0][0] = plife[0][0] + 1;
13521         pnameJ[0][5] = pnameJ[0][1] = picon[0][1];
13522         pnameJ[0][3] = 10 + pnameJ[0][5];
13523     }
13524     if(!pnameJused[1])
13525     {
13526         pnameJ[1][2] = pnameJ[1][4] = pnameJ[1][0] = plife[1][0] + 1;
13527         pnameJ[1][5] = pnameJ[1][1] = picon[1][1];
13528         pnameJ[1][3] = 10 + pnameJ[1][5];
13529     }
13530     if(!pnameJused[2])
13531     {
13532         pnameJ[2][2] = pnameJ[2][4] = pnameJ[2][0] = plife[2][0] + 1;
13533         pnameJ[2][5] = pnameJ[2][1] = picon[2][1];
13534         pnameJ[2][3] = 10 + pnameJ[2][5];
13535     }
13536     if(!pnameJused[3])
13537     {
13538         pnameJ[3][2] = pnameJ[3][4] = pnameJ[3][0] = plife[3][0] + 1;
13539         pnameJ[3][5] = pnameJ[3][1] = picon[3][1];
13540         pnameJ[3][3] = 10 + pnameJ[3][5];
13541     }
13542     for(i = 0; i < 4; i++) if(pnameJ[i][6] == -1)
13543         {
13544             pnameJ[i][6] = 0;
13545         }
13546 
13547     if(!pscoreUsed[0])
13548     {
13549         pscore[0][0] = plife[0][0] + 1;
13550         pscore[0][1] = picon[0][1];
13551     }
13552     if(!pscoreUsed[1])
13553     {
13554         pscore[1][0] = plife[1][0] + 1;
13555         pscore[1][1] = picon[1][1];
13556     }
13557     if(!pscoreUsed[2])
13558     {
13559         pscore[2][0] = plife[2][0] + 1;
13560         pscore[2][1] = picon[2][1];
13561     }
13562     if(!pscoreUsed[3])
13563     {
13564         pscore[3][0] = plife[3][0] + 1;
13565         pscore[3][1] = picon[3][1];
13566     }
13567     for(i = 0; i < 4; i++) if(pscore[i][6] == -1)
13568         {
13569             pscore[i][6] = 0;
13570         }
13571 
13572     if(!enameused[0])
13573     {
13574         ename[0][0] = elife[0][0] + 1;
13575         ename[0][1] = eicon[0][1];
13576     }
13577     if(!enameused[1])
13578     {
13579         ename[1][0] = elife[1][0] + 1;
13580         ename[1][1] = eicon[1][1];
13581     }
13582     if(!enameused[2])
13583     {
13584         ename[2][0] = ename[0][0];
13585         ename[2][1] = eicon[2][1];
13586     }
13587     if(!enameused[3])
13588     {
13589         ename[3][0] = ename[1][0];
13590         ename[3][1] = eicon[3][1];
13591     }
13592     for(i = 0; i < 4; i++) if(ename[i][2] == -1)
13593         {
13594             ename[i][2] = 0;
13595         }
13596 
13597     branch_name[0] = 0; //clear up branch name, so we can use it in game
13598 
13599     for(i = 0; i < 4; i++) if(pshoot[i][2] == -1)
13600         {
13601             pshoot[i][2] = 2;
13602         }
13603     if(timeloc[5] == -1)
13604     {
13605         timeloc[5] = 3;
13606     }
13607 
13608     if(!set)
13609     {
13610         errormessage = "No levels were loaded!";
13611     }
13612 
13613     //assume old mods have same maxplayers for all sets
13614     else if(!psmenu[0][0] && !psmenu[0][1])
13615     {
13616         for(i = 0; i < set->maxplayers; i++)
13617         {
13618             psmenu[i][0] = (set->maxplayers > 2) ? ((111 - (set->maxplayers * 14)) + ((i * (320 - (166 / set->maxplayers)) / set->maxplayers) + videomodes.hShift)) :
13619                            (83 + (videomodes.hShift / 2) + (i * (155 + videomodes.hShift)));
13620             psmenu[i][1] = 230 + videomodes.vShift;
13621             psmenu[i][2] = (set->maxplayers > 2) ? ((95 - (set->maxplayers * 14)) + ((i * (320 - (166 / set->maxplayers)) / set->maxplayers) + videomodes.hShift)) :
13622                            (67 + (videomodes.hShift / 2) + (i * (155 + videomodes.hShift)));
13623             psmenu[i][3] = 225 + videomodes.vShift;
13624         }
13625     }
13626 
13627 lCleanup:
13628 
13629     if(buf)
13630     {
13631         free(buf);
13632     }
13633 
13634     if(!savelevel)
13635     {
13636         savelevel = calloc(num_difficulties, sizeof(*savelevel));
13637     }
13638 
13639     if(errormessage)
13640     {
13641         shutdown(1, "load_levelorder ERROR in %s at %d, msg: %s\n", filename, line, errormessage);
13642     }
13643 }
13644 
13645 
free_level(s_level * lv)13646 void free_level(s_level *lv)
13647 {
13648     int i, j;
13649 
13650     if(!lv)
13651     {
13652         return;
13653     }
13654 
13655     //offload blending tables
13656     for(i = 0; i < lv->numpalettes; i++)
13657     {
13658         for(j = 0; j < MAX_BLENDINGS; j++)
13659         {
13660             if(lv->blendings[i][j])
13661             {
13662                 free(lv->blendings[i][j]);
13663             }
13664             lv->blendings[i][j] = NULL;
13665         }
13666     }
13667 
13668     //offload layers
13669     for(i = 1; i < lv->numlayers; i++)
13670     {
13671         if(lv->layers[i].gfx.handle && lv->layers[i].gfx.handle != background)
13672         {
13673             if(lv->layers[i].gfx.sprite->magic == sprite_magic &&
13674                lv->layers[i].gfx.sprite->mask != NULL)
13675             {
13676                 free(lv->layers[i].gfx.sprite->mask);
13677             }
13678             free(lv->layers[i].gfx.handle);
13679             lv->layers[i].gfx.handle = NULL;
13680         }
13681     }
13682 
13683     //offload textobjs
13684     for(i = 0; i < lv->numtextobjs; i++)
13685     {
13686         if(lv->textobjs[i].text)
13687         {
13688             free(lv->textobjs[i].text);
13689             lv->textobjs[i].text = NULL;
13690         }
13691     }
13692 
13693     //offload basemaps
13694     for(i = 0; i < lv->numbasemaps; i++)
13695     {
13696         if(lv->basemaps[i].map)
13697         {
13698             free(lv->basemaps[i].map);
13699         }
13700     }
13701 
13702     //offload scripts
13703     Script_Clear(&(lv->update_script), 2);
13704     Script_Clear(&(lv->updated_script), 2);
13705     Script_Clear(&(lv->key_script), 2);
13706     Script_Clear(&(lv->level_script), 2);
13707     Script_Clear(&(lv->endlevel_script), 2);
13708 
13709     for(i = 0; i < lv->numspawns; i++)
13710     {
13711         Script_Clear(&(lv->spawnpoints[i].spawnscript), 2);
13712     }
13713 
13714     if(lv->spawnpoints)
13715     {
13716         free(lv->spawnpoints);
13717     }
13718     if(lv->layers)
13719     {
13720         free(lv->layers);
13721     }
13722     if(lv->layersref)
13723     {
13724         free(lv->layersref);
13725     }
13726     if(lv->panels)
13727     {
13728         free(lv->panels);
13729     }
13730     if(lv->frontpanels)
13731     {
13732         free(lv->frontpanels);
13733     }
13734     if(lv->bglayers)
13735     {
13736         free(lv->bglayers);
13737     }
13738     if(lv->fglayers)
13739     {
13740         free(lv->fglayers);
13741     }
13742     if(lv->genericlayers)
13743     {
13744         free(lv->genericlayers);
13745     }
13746     if(lv->waters)
13747     {
13748         free(lv->waters);
13749     }
13750     if(lv->textobjs)
13751     {
13752         free(lv->textobjs);
13753     }
13754     if(lv->holes)
13755     {
13756         free(lv->holes);
13757     }
13758     if(lv->walls)
13759     {
13760         free(lv->walls);
13761     }
13762     if(lv->basemaps)
13763     {
13764         free(lv->basemaps);
13765     }
13766     if(lv->palettes)
13767     {
13768         free(lv->palettes);
13769     }
13770     if(lv->blendings)
13771     {
13772         free(lv->blendings);
13773     }
13774 
13775     free(lv);
13776     lv = NULL;
13777 }
13778 
13779 
unload_level()13780 void unload_level()
13781 {
13782     s_model *temp;
13783 
13784     kill_all();
13785     unload_background();
13786 
13787     if(level)
13788     {
13789 
13790         level->pos = 0;
13791         level->advancetime = 0;
13792         level->quake = 0;
13793         level->quaketime = 0;
13794         level->waiting = 0;
13795 
13796         printf("Level Unloading: '%s'\n", level->name);
13797         getRamStatus(BYTES);
13798         free(level->name);
13799         level->name = NULL;
13800         free_level(level);
13801         level = NULL;
13802         temp = getFirstModel();
13803         do
13804         {
13805             if(!temp)
13806             {
13807                 break;
13808             }
13809             if((temp->unload & 2))
13810             {
13811                 cache_model_sprites(temp, 0);
13812             }
13813             if((temp->unload & 1))
13814             {
13815                 free_model(temp);
13816                 temp = getCurrentModel();
13817             }
13818             else
13819             {
13820                 temp = getNextModel();
13821             }
13822         }
13823         while(temp);
13824         printf("RAM Status:\n");
13825         getRamStatus(BYTES);
13826 
13827 
13828     }
13829 
13830     advancex = 0;
13831     advancey = 0;
13832     nojoin = 0;
13833     current_spawn = 0;
13834     groupmin = 100;
13835     groupmax = 100;
13836     scrollminz = 0;
13837     scrollmaxz = 0;
13838     scrollminx = 0;
13839     scrollmaxx = 0;
13840     blockade = 0;
13841     level_completed = 0;
13842     tospeedup = 0;    // Reset so it sets to normal speed for the next level
13843     reached[0] = reached[1] = reached[2] = reached[3] = 0;    // TYPE_ENDLEVEL values reset after level completed //4player
13844     showtimeover = 0;
13845     pause = 0;
13846     endgame = 0;
13847     go_time = 0;
13848     debug_time = 0;
13849     debug_xy_msg.x = -1;
13850     debug_xy_msg.y = -1;
13851     neon_time = 0;
13852     time = 0;
13853     cameratype = 0;
13854     light.x = 128;
13855     light.y = 64;
13856     gfx_y_offset = gfx_x_offset = gfx_y_offset_adj = 0;    // Added so select screen graphics display correctly
13857 }
13858 
13859 
addhole(float x,float z,float x1,float x2,float x3,float x4,float depth,float alt,int type)13860 static void addhole(float x, float z, float x1, float x2, float x3, float x4, float depth, float alt, int type)
13861 {
13862     __realloc(level->holes, level->numholes);
13863     level->holes[level->numholes].x = x;
13864     level->holes[level->numholes].z = z;
13865     level->holes[level->numholes].upperleft = x1;
13866     level->holes[level->numholes].lowerleft = x2;
13867     level->holes[level->numholes].upperright = x3;
13868     level->holes[level->numholes].lowerright = x4;
13869     level->holes[level->numholes].depth = depth;
13870     level->holes[level->numholes].height = alt;
13871     level->holes[level->numholes].type = type;
13872 
13873     level->numholes++;
13874 }
13875 
addwall(float x,float z,float x1,float x2,float x3,float x4,float depth,float alt,int type)13876 static void addwall(float x, float z, float x1, float x2, float x3, float x4, float depth, float alt, int type)
13877 {
13878     __realloc(level->walls, level->numwalls);
13879     level->walls[level->numwalls].x = x;
13880     level->walls[level->numwalls].z = z;
13881     level->walls[level->numwalls].upperleft = x1;
13882     level->walls[level->numwalls].lowerleft = x2;
13883     level->walls[level->numwalls].upperright = x3;
13884     level->walls[level->numwalls].lowerright = x4;
13885     level->walls[level->numwalls].depth = depth;
13886     level->walls[level->numwalls].height = alt;
13887     level->walls[level->numwalls].type = type;
13888 
13889     level->numwalls++;
13890 }
13891 
addbasemap(float rx,float rz,float x_size,float z_size,float min_y,float max_y,int x_cont)13892 static void addbasemap(float rx, float rz, float x_size, float z_size, float min_y, float max_y, int x_cont)
13893 {
13894     __realloc(level->basemaps, level->numbasemaps);
13895     level->numbasemaps++;
13896     generate_basemap(level->numbasemaps-1, rx, rz, x_size, z_size, min_y, max_y, x_cont);
13897 }
13898 
generate_basemap(int map_index,float rx,float rz,float x_size,float z_size,float min_y,float max_y,int x_cont)13899 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) {
13900     float x, z;
13901 	float delta, y, tmp;
13902 	int dir = 0;
13903 
13904     if(map_index >= level->numbasemaps)
13905     {
13906         __reallocto(level->basemaps, level->numbasemaps, map_index + 1);
13907         level->numbasemaps = map_index + 1;
13908     }
13909 
13910     level->basemaps[map_index].position.x = rx;
13911     level->basemaps[map_index].position.z = rz-z_size;
13912     level->basemaps[map_index].size.x = x_size;
13913     level->basemaps[map_index].size.z = z_size;
13914 
13915     if(!level->basemaps[map_index].map)
13916     {
13917         level->basemaps[map_index].map = calloc( 1, (int)(sizeof(*(level->basemaps[map_index].map)) * (x_size+1)*(z_size+1)) );
13918     }
13919 
13920     if (min_y <= max_y) dir = 1;
13921     else
13922     {
13923         dir = 0;
13924         tmp = min_y;
13925         min_y = max_y;
13926         max_y = tmp;
13927     }
13928 
13929 	delta = (max_y - min_y) / ( (x_size <= 0) ? 1 : (x_size-1) );
13930 
13931 	for( x = 0; x < x_size; x++)
13932     {
13933 		if ( dir )
13934         {
13935             if ( x == x_size-1 ) y = max_y;
13936             else y = x*delta + min_y;
13937         }
13938 		else
13939         {
13940             y = max_y - (x*delta);
13941         }
13942 
13943 		if ( x_cont != 0 )
13944         {
13945             if ( dir > 0 )
13946             {
13947                 if ( x+rx >= x_cont ) y = max_y; // connect with the wall more smoothly
13948             }
13949             else
13950             {
13951                 if ( x+rx <= x_cont ) y = max_y;
13952             }
13953 		}
13954 
13955 		for ( z = 0; z < z_size; z++)
13956         {
13957 			level->basemaps[map_index].map[(int)(x + z*x_size)] = y;
13958 			//printf("map[%d] = %f\n",(int)(x + z*x_size),y);
13959 		}
13960 		//printf("x:%f y:%f delta:%f\n",x,y,delta);
13961 		//printf("y: %f\n",y);
13962 	}
13963 
13964 	return;
13965 }
13966 
load_level(char * filename)13967 void load_level(char *filename)
13968 {
13969     char *buf = NULL;
13970     size_t size, len, sblen;
13971     ptrdiff_t pos, oldpos;
13972     char *command;
13973     char *value;
13974     char *scriptbuf = NULL;
13975     char string[128] = {""};
13976     s_spawn_entry next;
13977     s_model *tempmodel, *cached_model;
13978 
13979     int i = 0, j = 0, crlf = 0;
13980     int usemap[MAX_BLENDINGS];
13981     char bgPath[128] = {""}, fnbuf[128];
13982     s_loadingbar bgPosi = {0, 0, {0,0}, {0,0}, 0, 0};
13983     char musicPath[128] = {""};
13984     u32 musicOffset = 0;
13985 
13986     ArgList arglist;
13987     char argbuf[MAX_ARG_LEN + 1] = "";
13988 
13989     ArgList arglist2;
13990     char argbuf2[MAX_ARG_LEN + 1] = "";
13991 
13992     levelCommands cmd;
13993     levelCommands cmd2;
13994     int line = 0;
13995     char *errormessage = NULL;
13996     char *scriptname = NULL;
13997     Script *tempscript = NULL;
13998     s_drawmethod *dm;
13999     s_layer *bgl;
14000     int (*panels)[3] = NULL;
14001     int *order = NULL;
14002     int panelcount = 0;
14003     int exit_blocked = 0, exit_hole = 0;
14004     char maskPath[128] = {""};
14005 
14006     unload_level();
14007 
14008     printf("Level Loading:   '%s'\n", filename);
14009 
14010 
14011 
14012     getRamStatus(BYTES);
14013 
14014     if(isLoadingScreenTypeBg(loadingbg[1].set))
14015     {
14016         if(custBkgrds)
14017         {
14018             strcpy(string, custBkgrds);
14019             strcat(string, "loading2");
14020             load_background(string, 0);
14021         }
14022         else
14023         {
14024             load_cached_background("data/bgs/loading2", 0);
14025         }
14026         clearscreen(vscreen);
14027         spriteq_clear();
14028         standard_palette(1);
14029     }
14030 
14031     if(isLoadingScreenTypeBar(loadingbg[1].set))
14032     {
14033         lifebar_colors();
14034         init_colourtable();
14035     }
14036 
14037     update_loading(&loadingbg[1], -1, 1); // initialize the update screen
14038 
14039     memset(&next, 0, sizeof(next));
14040 
14041     level = calloc(1, sizeof(*level));
14042     if(!level)
14043     {
14044         errormessage = "load_level() #1 FATAL: Out of memory!";
14045         goto lCleanup;
14046     }
14047     len = strlen(filename);
14048     level->name = malloc(len + 1);
14049 
14050     if(!level->name)
14051     {
14052         errormessage = "load_level() #1 FATAL: Out of memory!";
14053         goto lCleanup;
14054     }
14055     strcpy(level->name, filename);
14056 
14057     if(buffer_pakfile(filename, &buf, &size) != 1)
14058     {
14059         errormessage = "Unable to load level file!";
14060         goto lCleanup;
14061     }
14062 
14063     level->settime = 100;    // Feb 25, 2005 - Default time limit set to 100
14064     level->nospecial = 0;    // Default set to specials can be used during bonus levels
14065     level->nohurt = 0;    // Default set to players can hurt each other during bonus levels
14066     level->nohit = 0;    // Default able to hit the other player
14067     level->spawn[0].y = level->spawn[1].y = level->spawn[2].y = level->spawn[3].y = 300;    // Set the default spawn y to 300
14068     level->setweap = 0;
14069     level->maxtossspeed = default_level_maxtossspeed;
14070     level->maxfallspeed = default_level_maxfallspeed;
14071     level->gravity = default_level_gravity;
14072     level->scrolldir = SCROLL_RIGHT;
14073     level->scrollspeed = 1;
14074     level->cameraxoffset = 0;
14075     level->camerazoffset = 0;
14076     level->bosses = 0;
14077     blendfx[BLEND_MULTIPLY] = 1;
14078     bgtravelled = 0;
14079     vbgtravelled = 0;
14080     traveltime = 0;
14081     texttime = 0;
14082     nopause = 0;
14083     nofadeout = 0;
14084     noscreenshot = 0;
14085     panel_width = panel_height = frontpanels_loaded = 0;
14086 
14087     //reset_playable_list(1);
14088 
14089     // Now interpret the contents of buf line by line
14090     pos = 0;
14091     while(pos < size)
14092     {
14093         line++;
14094         ParseArgs(&arglist, buf + pos, argbuf);
14095         command = GET_ARG(0);
14096         cmd = getLevelCommand(levelcmdlist, command);
14097         switch(cmd)
14098         {
14099         case CMD_LEVEL_LOADINGBG:
14100             load_background(GET_ARG(1), 0);
14101             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));
14102             if (errormessage)
14103             {
14104                 goto lCleanup;
14105             }
14106             standard_palette(1);
14107             lifebar_colors();
14108             init_colourtable();
14109             update_loading(&bgPosi, -1, 1); // initialize the update screen
14110             break;
14111         case CMD_LEVEL_MUSICFADE:
14112             memset(&next, 0, sizeof(next));
14113             next.musicfade = GET_FLOAT_ARG(1);
14114             break;
14115         case CMD_LEVEL_MUSIC:
14116             value = GET_ARG(1);
14117             strncpy(string, value, 128);
14118             musicOffset = atol(GET_ARG(2));
14119             if(loadingmusic)
14120             {
14121                 music(string, 1, musicOffset);
14122                 musicPath[0] = 0;
14123             }
14124             else
14125             {
14126                 oldpos = pos;
14127                 // Go to next line
14128                 pos += getNewLineStart(buf + pos);
14129 #define GET_ARG2(z) arglist2.count > z ? arglist2.args[z] : ""
14130                 if(pos < size)
14131                 {
14132                     ParseArgs(&arglist2, buf + pos, argbuf2);
14133                     command = GET_ARG2(0);
14134                     cmd2 = getLevelCommand(levelcmdlist, command);
14135                 }
14136                 else
14137                 {
14138                     cmd2 = (levelCommands) 0;
14139                 }
14140 
14141                 if(cmd2 == CMD_LEVEL_AT)
14142                 {
14143                     if(next.musicfade == 0)
14144                     {
14145                         memset(&next, 0, sizeof(next));
14146                     }
14147                     strncpy(next.music, string, 128);
14148                     next.musicoffset = musicOffset;
14149                 }
14150                 else
14151                 {
14152                     strncpy(musicPath, string, 128);
14153                 }
14154                 pos = oldpos;
14155 #undef GET_ARG2
14156             }
14157             break;
14158         case CMD_LEVEL_ALLOWSELECT:
14159             load_playable_list(buf + pos);
14160             break;
14161         case CMD_LEVEL_LOAD:
14162 #ifdef DEBUG
14163             printf("load_level: load %s, %s\n", GET_ARG(1), filename);
14164 #endif
14165             tempmodel = findmodel(GET_ARG(1));
14166             if (!tempmodel)
14167             {
14168                 load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2));
14169             }
14170             else
14171             {
14172                 update_model_loadflag(tempmodel, GET_INT_ARG(2));
14173             }
14174             break;
14175         case CMD_LEVEL_ALPHAMASK:
14176             strncpy(maskPath, GET_ARG(1), 128);
14177             break;
14178         case CMD_LEVEL_BACKGROUND:
14179         case CMD_LEVEL_BGLAYER:
14180         case CMD_LEVEL_LAYER:
14181         case CMD_LEVEL_FGLAYER:
14182             __realloc(level->layers, level->numlayers);
14183             bgl = &(level->layers[level->numlayers]);
14184 
14185             if(cmd == CMD_LEVEL_BACKGROUND || cmd == CMD_LEVEL_BGLAYER)
14186             {
14187                 i = 0;
14188                 bgl->z = MIN_INT;
14189             }
14190             else
14191             {
14192                 i = 1;
14193                 bgl->z =  GET_FLOAT_ARG(2);
14194                 if(cmd == CMD_LEVEL_FGLAYER)
14195                 {
14196                     bgl->z += FRONTPANEL_Z;
14197                 }
14198             }
14199 
14200             if(cmd == CMD_LEVEL_BACKGROUND)
14201             {
14202                 if(bgPath[0])
14203                 {
14204                     errormessage = "Background is already defined!";
14205                     goto lCleanup;
14206                 }
14207                 value = GET_ARG(1);
14208                 strcpy(bgPath, value);
14209                 bgl->oldtype = BGT_BACKGROUND;
14210             }
14211             else if(cmd == CMD_LEVEL_BGLAYER)
14212             {
14213                 bgl->oldtype = BGT_BGLAYER;
14214             }
14215             else if(cmd == CMD_LEVEL_FGLAYER)
14216             {
14217                 bgl->oldtype = BGT_FGLAYER;
14218             }
14219             else if(cmd == CMD_LEVEL_LAYER)
14220             {
14221                 bgl->oldtype = BGT_GENERIC;
14222             }
14223 
14224             dm = &(bgl->drawmethod);
14225             *dm = plainmethod;
14226 
14227             bgl->ratio.x = GET_FLOAT_ARG(i + 2); // x ratio
14228             bgl->ratio.z = GET_FLOAT_ARG(i + 3); // z ratio
14229             bgl->offset.x = GET_INT_ARG(i + 4); // x start
14230             bgl->offset.z = GET_INT_ARG(i + 5); // z start
14231             bgl->spacing.x = GET_INT_ARG(i + 6); // x spacing
14232             bgl->spacing.z = GET_INT_ARG(i + 7); // z spacing
14233             dm->xrepeat = GET_INT_ARG(i + 8); // x repeat
14234             dm->yrepeat = GET_INT_ARG(i + 9); // z repeat
14235             dm->transbg = GET_INT_ARG(i + 10); // transparency
14236             dm->alpha = GET_INT_ARG(i + 11); // alpha
14237             dm->water.watermode = GET_INT_ARG(i + 12); // water
14238             if(dm->water.watermode == 3)
14239             {
14240                 dm->water.beginsize = GET_FLOAT_ARG(i + 13); // beginsize
14241                 dm->water.endsize = GET_FLOAT_ARG(i + 14); // endsize
14242                 dm->water.perspective = GET_INT_ARG(i + 15); // amplitude
14243             }
14244             else
14245             {
14246                 dm->water.amplitude = GET_INT_ARG(i + 13); // amplitude
14247                 dm->water.wavelength = GET_FLOAT_ARG(i + 14); // wavelength
14248                 dm->water.wavespeed = GET_FLOAT_ARG(i + 15); // waterspeed
14249             }
14250             bgl->bgspeedratio = GET_FLOAT_ARG(i + 16); // moving
14251             bgl->quake = GET_INT_ARG(i + 17); // quake
14252             bgl->neon = GET_INT_ARG(i + 18); // neon
14253             bgl->enabled = 1; // enabled
14254 
14255             if((GET_ARG(i + 2))[0] == 0)
14256             {
14257                 bgl->ratio.x = (cmd == CMD_LEVEL_FGLAYER ? 1.5 : 0.5);
14258             }
14259             if((GET_ARG(i + 3))[0] == 0)
14260             {
14261                 bgl->ratio.z = (cmd == CMD_LEVEL_FGLAYER ? 1.5 : 0.5);
14262             }
14263 
14264             if((GET_ARG(i + 8))[0] == 0)
14265             {
14266                 dm->xrepeat = -1;
14267             }
14268             if((GET_ARG(i + 9))[0] == 0)
14269             {
14270                 dm->yrepeat = -1;
14271             }
14272             if(cmd == CMD_LEVEL_BACKGROUND && (GET_ARG(i + 16))[0] == 0)
14273             {
14274                 bgl->bgspeedratio = 1.0;
14275             }
14276 
14277             if(blendfx_is_set == 0 && dm->alpha)
14278             {
14279                 blendfx[dm->alpha - 1] = 1;
14280             }
14281 
14282             if(cmd != CMD_LEVEL_BACKGROUND)
14283             {
14284                 load_layer(GET_ARG(1), maskPath, level->numlayers);
14285             }
14286             level->numlayers++;
14287             break;
14288         case CMD_LEVEL_WATER:
14289             __realloc(level->layers, level->numlayers);
14290             bgl = &(level->layers[level->numlayers]);
14291             dm = &(bgl->drawmethod);
14292             *dm = plainmethod;
14293 
14294             bgl->oldtype = BGT_WATER;
14295             bgl->z = MIN_INT + 1;
14296 
14297             bgl->ratio.x = 0.5; // x ratio
14298             bgl->ratio.z = 0.5; // z ratio
14299             bgl->offset.x = 0; // x start
14300             bgl->offset.z = NaN; // z start
14301             bgl->spacing.x = 0; // x spacing
14302             bgl->spacing.z = 0; // z spacing
14303             dm->xrepeat = -1; // x repeat
14304             dm->yrepeat = 1; // z repeat
14305             dm->transbg = 0; // transparency
14306             dm->alpha = 0; // alpha
14307             dm->water.watermode = 2; // amplitude
14308             dm->water.amplitude = GET_INT_ARG(2); // amplitude
14309             dm->water.wavelength = 40; // wavelength
14310             dm->water.wavespeed = 1.0; // waterspeed
14311             bgl->bgspeedratio = 0; // moving
14312             bgl->enabled = 1; // enabled
14313 
14314             if(dm->water.amplitude < 1)
14315             {
14316                 dm->water.amplitude = 1;
14317             }
14318 
14319             load_layer(GET_ARG(1), maskPath, level->numlayers);
14320             level->numlayers++;
14321             break;
14322         case CMD_LEVEL_DIRECTION:
14323             value = GET_ARG(1);
14324             if(stricmp(value, "up") == 0)
14325             {
14326                 level->scrolldir = SCROLL_UP;
14327             }
14328             else if(stricmp(value, "down") == 0)
14329             {
14330                 level->scrolldir = SCROLL_DOWN;
14331             }
14332             else if(stricmp(value, "left") == 0)
14333             {
14334                 level->scrolldir = SCROLL_LEFT;
14335             }
14336             else if(stricmp(value, "both") == 0 || stricmp(value, "rightleft") == 0)
14337             {
14338                 level->scrolldir = SCROLL_BOTH;
14339             }
14340             else if(stricmp(value, "leftright") == 0)
14341             {
14342                 level->scrolldir = SCROLL_LEFTRIGHT;
14343             }
14344             else if(stricmp(value, "right") == 0)
14345             {
14346                 level->scrolldir = SCROLL_RIGHT;
14347             }
14348             else if(stricmp(value, "in") == 0)
14349             {
14350                 level->scrolldir = SCROLL_INWARD;
14351             }
14352             else if(stricmp(value, "out") == 0)
14353             {
14354                 level->scrolldir = SCROLL_OUTWARD;
14355             }
14356             else if(stricmp(value, "inout") == 0)
14357             {
14358                 level->scrolldir = SCROLL_INOUT;
14359             }
14360             else if(stricmp(value, "outin") == 0)
14361             {
14362                 level->scrolldir = SCROLL_OUTIN;
14363             }
14364             break;
14365         case CMD_LEVEL_FACING:
14366             level->facing = GET_INT_ARG(1);
14367             break;
14368         case CMD_LEVEL_ROCK:
14369             level->rocking = GET_INT_ARG(1);
14370             break;
14371         case CMD_LEVEL_BGSPEED:
14372             level->bgspeed = GET_FLOAT_ARG(1);
14373             if(GET_INT_ARG(2))
14374             {
14375                 level->bgspeed *= -1;
14376             }
14377             break;
14378         case CMD_LEVEL_VBGSPEED:
14379             level->vbgspeed = GET_FLOAT_ARG(1);
14380             if(GET_INT_ARG(2))
14381             {
14382                 level->vbgspeed *= -1;
14383             }
14384             break;
14385         case CMD_LEVEL_SCROLLSPEED:
14386             level->scrollspeed = GET_FLOAT_ARG(1);
14387             break;
14388         case CMD_LEVEL_MIRROR:
14389             level->mirror = GET_INT_ARG(1);
14390             break;
14391         case CMD_LEVEL_BOSSMUSIC:
14392             strncpy(level->bossmusic, GET_ARG(1), 255);
14393             level->bossmusic_offset = atol(GET_ARG(2));
14394             break;
14395         case CMD_LEVEL_NOSAVE:
14396             nosave = GET_INT_ARG(1);
14397             break;
14398         case CMD_LEVEL_NOFADEOUT:
14399             nofadeout = GET_INT_ARG(1);
14400             break;
14401         case CMD_LEVEL_NOPAUSE:
14402             nopause = GET_INT_ARG(1);
14403             break;
14404         case CMD_LEVEL_NOSCREENSHOT:
14405             noscreenshot = GET_INT_ARG(1);
14406             break;
14407         case CMD_LEVEL_SETTIME:
14408             // If settime is found, overwrite the default 100 for time limit
14409             level->settime = GET_INT_ARG(1);
14410             if(level->settime > 100 || level->settime < 0)
14411             {
14412                 level->settime = 100;
14413             }
14414             // Feb 25, 2005 - Time limit loaded from individual .txt file
14415             break;
14416         case CMD_LEVEL_SETWEAP:
14417             // Specify a weapon for each level
14418             level->setweap = GET_INT_ARG(1);
14419             break;
14420         case CMD_LEVEL_NOTIME:
14421             // Flag to if the time should be displayed 1 = no, else yes
14422             level->notime = GET_INT_ARG(1);
14423             break;
14424         case CMD_LEVEL_NORESET:
14425             // Flag to if the time should be reset when players respawn 1 = no, else yes
14426             level->noreset = GET_INT_ARG(1);
14427             break;
14428         case CMD_LEVEL_NOSLOW:
14429             // If set, level will not slow down when bosses are defeated
14430             level->noslow = GET_INT_ARG(1);
14431             break;
14432         case CMD_LEVEL_TYPE:
14433             level->type = GET_INT_ARG(1);    // Level type - 1 = bonus, else regular
14434             level->nospecial = GET_INT_ARG(2);    // Can use specials during bonus levels (default 0 - yes)
14435             level->nohurt = GET_INT_ARG(3);    // Can hurt other players during bonus levels (default 0 - yes)
14436             break;
14437         case CMD_LEVEL_NOHIT:
14438             level->nohit = GET_INT_ARG(1);
14439             break;
14440         case CMD_LEVEL_GRAVITY:
14441             level->gravity = GET_FLOAT_ARG(1);
14442             level->gravity /= 100;
14443             break;
14444         case CMD_LEVEL_MAXFALLSPEED:
14445             level->maxfallspeed = GET_FLOAT_ARG(1);
14446             level->maxfallspeed /= 10;
14447             break;
14448         case CMD_LEVEL_MAXTOSSSPEED:
14449             level->maxtossspeed = GET_FLOAT_ARG(1);
14450             level->maxtossspeed /= 10;
14451             break;
14452         case CMD_LEVEL_CAMERATYPE:
14453             cameratype = GET_INT_ARG(1);
14454             break;
14455         case CMD_LEVEL_CAMERAOFFSET:
14456             level->cameraxoffset = GET_INT_ARG(1);
14457             level->camerazoffset = GET_INT_ARG(2);
14458             break;
14459         case CMD_LEVEL_SPAWN1:
14460         case CMD_LEVEL_SPAWN2:
14461         case CMD_LEVEL_SPAWN3:
14462         case CMD_LEVEL_SPAWN4:
14463             switch(cmd)
14464             {
14465             case CMD_LEVEL_SPAWN1:
14466                 i = 0;
14467                 break;
14468             case CMD_LEVEL_SPAWN2:
14469                 i = 1;
14470                 break;
14471             case CMD_LEVEL_SPAWN3:
14472                 i = 2;
14473                 break;
14474             case CMD_LEVEL_SPAWN4:
14475                 i = 3;
14476                 break;
14477             default:
14478                 assert(0);
14479             }
14480             level->spawn[i].x = GET_INT_ARG(1);
14481             level->spawn[i].z = GET_INT_ARG(2);
14482             level->spawn[i].y = GET_INT_ARG(3);
14483 
14484             //if(level->spawn[i].z > 232 || level->spawn[i].z < 0) level->spawn[i].z= 232;
14485             if(level->spawn[i].y < 0)
14486             {
14487                 level->spawn[i].y = 300;
14488             }
14489             break;
14490         case CMD_LEVEL_FRONTPANEL:
14491         case CMD_LEVEL_PANEL:
14492             if(level->numlayers == 0)
14493             {
14494                 __realloc(level->layers, level->numlayers);
14495                 level->numlayers = 1; // reserve for background
14496             }
14497 
14498             __realloc(level->layers, level->numlayers);
14499             bgl = &(level->layers[level->numlayers]);
14500             dm = &(bgl->drawmethod);
14501             *dm = plainmethod;
14502 
14503             bgl->oldtype = (cmd == CMD_LEVEL_FRONTPANEL ? BGT_FRONTPANEL : BGT_PANEL);
14504 
14505             if(bgl->oldtype == BGT_PANEL)
14506             {
14507                 bgl->order = panelcount + 1;
14508                 __realloc(panels, panelcount);
14509                 panels[panelcount++][0] = level->numlayers;
14510                 bgl->z = PANEL_Z;
14511                 bgl->ratio.x = 0; // x ratio
14512                 bgl->ratio.z = 0; // z ratio
14513                 dm->xrepeat = 1; // x repeat
14514             }
14515             else
14516             {
14517                 frontpanels_loaded++;
14518                 bgl->z = FRONTPANEL_Z;
14519                 bgl->ratio.x = -0.4; // x ratio
14520                 bgl->ratio.z = 1; // z ratio
14521                 dm->xrepeat = -1; // x repeat
14522             }
14523 
14524             bgl->bgspeedratio = 0;
14525             bgl->offset.z = 0;
14526             dm->yrepeat = 1; // z repeat
14527             dm->transbg = 1; // transparency
14528             bgl->enabled = 1; // enabled
14529             bgl->quake = 1; // accept quake and rock
14530 
14531             load_layer(GET_ARG(1), maskPath, level->numlayers);
14532             level->numlayers++;
14533 
14534             if(stricmp(GET_ARG(2), "none") != 0 && GET_ARG(2)[0])
14535             {
14536                 __realloc(level->layers, level->numlayers);
14537                 bgl = &(level->layers[level->numlayers]);
14538                 *bgl = *(bgl - 1);
14539                 panels[panelcount - 1][1] = level->numlayers;
14540 
14541                 bgl->z = NEONPANEL_Z;
14542                 bgl->neon = 1;
14543                 bgl->gfx.handle = NULL;
14544                 load_layer(GET_ARG(2), maskPath, level->numlayers);
14545                 level->numlayers++;
14546             }
14547 
14548             if(stricmp(GET_ARG(3), "none") != 0 && GET_ARG(3)[0])
14549             {
14550                 __realloc(level->layers, level->numlayers);
14551                 bgl = &(level->layers[level->numlayers]);
14552                 *bgl = *(bgl - 1);
14553                 panels[panelcount - 1][2] = level->numlayers;
14554                 dm = &(bgl->drawmethod);
14555 
14556                 bgl->z = SCREENPANEL_Z;
14557                 bgl->neon = 0;
14558                 dm->alpha = 1;
14559                 bgl->gfx.handle = NULL;
14560                 load_layer(GET_ARG(3), maskPath, level->numlayers);
14561                 level->numlayers++;
14562             }
14563             break;
14564         case CMD_LEVEL_STAGENUMBER:
14565             current_stage = GET_INT_ARG(1);
14566             break;
14567         case CMD_LEVEL_ORDER:
14568             // Append to order
14569             value = GET_ARG(1);
14570             i = 0;
14571             while(value[i] )
14572             {
14573                 j = value[i];
14574                 // WTF ?
14575                 if(j >= 'A' && j <= 'Z')
14576                 {
14577                     j -= 'A';
14578                 }
14579                 else if(j >= 'a' && j <= 'z')
14580                 {
14581                     j -= 'a';
14582                 }
14583                 else
14584                 {
14585                     errormessage = "Illegal character in panel order!";
14586                     goto lCleanup;
14587                 }
14588                 __realloc(order, level->numpanels);
14589                 __realloc(level->panels, level->numpanels);
14590                 order[level->numpanels] = j;
14591                 level->numpanels++;
14592                 i++;
14593             }
14594             break;
14595         case CMD_LEVEL_HOLE:
14596             value = GET_ARG(1);    // ltb    1-18-05  adjustable hole sprites
14597 
14598             if(holesprite < 0)
14599             {
14600                 if(testpackfile(value, packfile) >= 0)
14601                 {
14602                     holesprite = loadsprite(value, 0, 0, pixelformat);    // ltb 1-18-05  load new hole sprite
14603                 }
14604                 else
14605                 {
14606                     holesprite = loadsprite("data/sprites/hole", 0, 0, pixelformat);    // ltb 1-18-05  no new sprite load the default
14607                 }
14608             }
14609 
14610             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));
14611             break;
14612         case CMD_LEVEL_WALL:
14613             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));
14614             break;
14615         case CMD_LEVEL_BASEMAP:
14616             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));
14617             break;
14618         case CMD_LEVEL_PALETTE:
14619             __realloc(level->palettes, level->numpalettes);
14620             __realloc(level->blendings, level->numpalettes);
14621             for(i = 0; i < MAX_BLENDINGS; i++)
14622             {
14623                 usemap[i] = GET_INT_ARG(i + 2);
14624             }
14625             if(!load_palette(level->palettes[level->numpalettes], GET_ARG(1)) ||
14626                     !create_blending_tables(level->palettes[level->numpalettes], level->blendings[level->numpalettes], usemap))
14627             {
14628                 errormessage = "Failed to create colour conversion tables for level! (Out of memory?)";
14629                 goto lCleanup;
14630             }
14631             level->numpalettes++;
14632             break;
14633         case CMD_LEVEL_UPDATESCRIPT:
14634         case CMD_LEVEL_UPDATEDSCRIPT:
14635         case CMD_LEVEL_KEYSCRIPT:
14636         case CMD_LEVEL_LEVELSCRIPT:
14637         case CMD_LEVEL_ENDLEVELSCRIPT:
14638             switch(cmd)
14639             {
14640             case CMD_LEVEL_UPDATESCRIPT:
14641                 tempscript = &(level->update_script);
14642                 scriptname = "levelupdatescript";
14643                 break;
14644             case CMD_LEVEL_UPDATEDSCRIPT:
14645                 tempscript = &(level->updated_script);
14646                 scriptname = "levelupdatedscript";
14647                 break;
14648             case CMD_LEVEL_KEYSCRIPT:
14649                 tempscript = &(level->key_script);
14650                 scriptname = "levelkeyscript";
14651                 break;
14652             case CMD_LEVEL_LEVELSCRIPT:
14653                 tempscript = &(level->level_script);
14654                 scriptname = "levelscript";
14655                 break;
14656             case CMD_LEVEL_ENDLEVELSCRIPT:
14657                 tempscript = &(level->endlevel_script);
14658                 scriptname = "endlevelscript";
14659                 break;
14660             default:
14661                 assert(0);
14662 
14663             }
14664             // this function is for model script, but it is OK for now
14665             pos += lcmHandleCommandScripts(&arglist, buf + pos, tempscript, scriptname, filename, 1, 1);
14666             break;
14667         case CMD_LEVEL_BLOCKED:
14668             exit_blocked = GET_INT_ARG(1);
14669             break;
14670         case CMD_LEVEL_ENDHOLE:
14671             exit_hole = GET_INT_ARG(1);
14672             break;
14673         case CMD_LEVEL_WAIT:
14674             // Clear spawn thing, set wait state instead
14675             memset(&next, 0, sizeof(next));
14676             next.wait = 1;
14677             break;
14678         case CMD_LEVEL_NOJOIN:
14679             // Clear spawn thing, set nojoin state instead
14680             memset(&next, 0, sizeof(next));
14681             next.nojoin = 1;
14682             break;
14683         case CMD_LEVEL_CANJOIN:
14684             memset(&next, 0, sizeof(next));
14685             next.nojoin = -1;
14686             break;
14687         case CMD_LEVEL_SHADOWCOLOR:
14688             memset(&next, 0, sizeof(next));
14689             next.shadowcolor = parsecolor(GET_ARG(1));
14690             break;
14691         case CMD_LEVEL_SHADOWALPHA:
14692             memset(&next, 0, sizeof(next));
14693             next.shadowalpha = GET_INT_ARG(1);
14694             if(blendfx_is_set == 0 && next.shadowalpha > 0)
14695             {
14696                 blendfx[next.shadowalpha - 1] = 1;
14697             }
14698             break;
14699         case CMD_LEVEL_SHADOWOPACITY:
14700             memset(&next, 0, sizeof(next));
14701             next.shadowopacity = GET_INT_ARG(1);
14702             break;
14703         case CMD_LEVEL_LIGHT:
14704             memset(&next, 0, sizeof(next));
14705             next.light.x = GET_INT_ARG(1);
14706             next.light.y = GET_INT_ARG(2);
14707             if(next.light.y == 0)
14708             {
14709                 next.light.y = 64;
14710             }
14711             break;
14712         case CMD_LEVEL_SCROLLZ:
14713             memset(&next, 0, sizeof(next));
14714             next.scrollminz = GET_INT_ARG(1);
14715             next.scrollmaxz = GET_INT_ARG(2);
14716             next.scrollminz |= 0x80000000;
14717             break;
14718         case CMD_LEVEL_SCROLLX:
14719             //shall we keep blockade?
14720             memset(&next, 0, sizeof(next));
14721             next.scrollminx = GET_INT_ARG(1);
14722             next.scrollmaxx = GET_INT_ARG(2);
14723             next.scrollminx |= 0x80000000;
14724             break;
14725         case CMD_LEVEL_BLOCKADE:
14726             // now x scroll can be limited by this
14727             memset(&next, 0, sizeof(next));
14728             next.blockade = GET_INT_ARG(1);
14729             if(next.blockade == 0)
14730             {
14731                 next.blockade = -1;
14732             }
14733             break;
14734         case CMD_LEVEL_SETPALETTE:
14735             // change system palette
14736             memset(&next, 0, sizeof(next));
14737             next.palette = GET_INT_ARG(1);
14738             break;
14739         case CMD_LEVEL_GROUP:
14740             // Clear spawn thing, set group instead
14741             memset(&next, 0, sizeof(next));
14742             next.groupmin = GET_INT_ARG(1);
14743             next.groupmax = GET_INT_ARG(2);
14744             if(next.groupmax < 1)
14745             {
14746                 next.groupmax = 1;
14747             }
14748             if(next.groupmin < 1)
14749             {
14750                 next.groupmin = 100;
14751             }
14752             break;
14753         case CMD_LEVEL_SPAWN:
14754             // Back to defaults
14755             next.spawnplayer_count = 0;
14756             memset(&next, 0, sizeof(next));
14757             next.index = next.itemindex = next.weaponindex = -1;
14758             // Name of entry to be spawned
14759             // Load model (if not loaded already)
14760             cached_model = findmodel(GET_ARG(1));
14761 #ifdef DEBUG
14762             printf("load_level: spawn %s, %s, cached: %p\n", GET_ARG(1), filename, cached_model);
14763 #endif
14764             if(cached_model)
14765             {
14766                 tempmodel = cached_model;
14767             }
14768             else
14769             {
14770                 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
14771             }
14772             if(tempmodel)
14773             {
14774                 next.name = tempmodel->name;
14775                 next.index = get_cached_model_index(next.name);
14776                 next.spawntype = 1;     //2011_03_23, DC; Spawntype 1 (level spawn).
14777                 crlf = 1;
14778             }
14779             break;
14780         case CMD_LEVEL_2PSPAWN:
14781             // Entity only for 2p game
14782             next.spawnplayer_count = 1;
14783             break;
14784         case CMD_LEVEL_3PSPAWN:
14785             // Entity only for 3p game
14786             next.spawnplayer_count = 2;
14787             break;
14788         case CMD_LEVEL_4PSPAWN:
14789             // Entity only for 4p game
14790             next.spawnplayer_count = 3;
14791             break;
14792         case CMD_LEVEL_BOSS:
14793             next.boss = GET_INT_ARG(1);
14794             level->bosses += next.boss ? 1 : 0;
14795             break;
14796         case CMD_LEVEL_FLIP:
14797             next.flip = GET_INT_ARG(1);
14798             break;
14799         case CMD_LEVEL_HEALTH:
14800             next.health[0] = next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1);
14801             break;
14802         case CMD_LEVEL_2PHEALTH:
14803             // Health the spawned entity will have if 2 people are playing
14804             next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1);
14805             break;
14806         case CMD_LEVEL_3PHEALTH:
14807             // Health the spawned entity will have if 2 people are playing
14808             next.health[2] = next.health[3] = GET_INT_ARG(1);  //4player
14809             break;
14810         case CMD_LEVEL_4PHEALTH:
14811             // Health the spawned entity will have if 2 people are playing
14812             next.health[3] = GET_INT_ARG(1);  //4player
14813             break;
14814         case CMD_LEVEL_MP:
14815             // mp values to put max mp for player by tails
14816             next.mp = GET_INT_ARG(1);
14817             break;
14818         case CMD_LEVEL_SCORE:
14819             // So score can be overriden in the levels .txt file
14820             next.score = GET_INT_ARG(1);
14821             if(next.score == -1)
14822             {
14823                 next.score = 0;    // So negative values cannot be added
14824             }
14825             next.multiple = GET_INT_ARG(2);
14826             if(next.multiple == -1)
14827             {
14828                 next.multiple = 0;    // So negative values cannot be added
14829             }
14830             break;
14831         case CMD_LEVEL_NOLIFE:
14832             // Flag to determine if entity life is shown when hit
14833             next.nolife = GET_INT_ARG(1);
14834             break;
14835         case CMD_LEVEL_ALIAS:
14836             // Alias (name displayed) of entry to be spawned
14837             strncpy(next.alias, GET_ARG(1), MAX_NAME_LEN);
14838             break;
14839         case CMD_LEVEL_MAP:
14840             // Colourmap for new entry
14841             next.colourmap = GET_INT_ARG(1);
14842             break;
14843         case CMD_LEVEL_ALPHA:
14844             // Item to be contained by new entry
14845             next.alpha = GET_INT_ARG(1);
14846             if(blendfx_is_set == 0 && next.alpha)
14847             {
14848                 blendfx[next.alpha - 1] = 1;
14849             }
14850             break;
14851         case CMD_LEVEL_DYING:
14852             // Used to store which remake corresponds with the dying flash
14853             next.dying = GET_INT_ARG(1);
14854             next.per1 = GET_INT_ARG(2);
14855             next.per2 = GET_INT_ARG(3);
14856             next.dying2 = GET_INT_ARG(4);
14857             break;
14858         case CMD_LEVEL_ITEM:
14859         case CMD_LEVEL_2PITEM:
14860         case CMD_LEVEL_3PITEM:
14861         case CMD_LEVEL_4PITEM:
14862             switch(cmd)
14863             {
14864                 // Item to be contained by new entry
14865             case CMD_LEVEL_ITEM:
14866                 next.itemplayer_count = 0;
14867                 break;
14868             case CMD_LEVEL_2PITEM:
14869                 next.itemplayer_count = 1;
14870                 break;
14871             case CMD_LEVEL_3PITEM:
14872                 next.itemplayer_count = 2;
14873                 break;
14874             case CMD_LEVEL_4PITEM:
14875                 next.itemplayer_count = 3;
14876                 break;
14877             default:
14878                 assert(0);
14879             }
14880             // Load model (if not loaded already)
14881             cached_model = findmodel(GET_ARG(1));
14882             if(cached_model)
14883             {
14884                 tempmodel = cached_model;
14885             }
14886             else
14887             {
14888                 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
14889             }
14890             if(tempmodel)
14891             {
14892                 next.item = tempmodel->name;
14893                 next.itemindex = get_cached_model_index(next.item);
14894             }
14895             break;
14896         case CMD_LEVEL_ITEMMAP:
14897             next.itemmap = GET_INT_ARG(1);
14898             break;
14899         case CMD_LEVEL_ITEMHEALTH:
14900             next.itemhealth = GET_INT_ARG(1);
14901             break;
14902         case CMD_LEVEL_ITEMALIAS:
14903             strncpy(next.itemalias, GET_ARG(1), MAX_NAME_LEN);
14904             break;
14905         case CMD_LEVEL_WEAPON:
14906             //spawn with a weapon 2007-2-12 by UTunnels
14907             // Load model (if not loaded already)
14908             cached_model = findmodel(GET_ARG(1));
14909             if(cached_model)
14910             {
14911                 tempmodel = cached_model;
14912             }
14913             else
14914             {
14915                 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
14916             }
14917             if(tempmodel)
14918             {
14919                 next.weapon = tempmodel->name;
14920                 next.weaponindex = get_cached_model_index(next.weapon);
14921             }
14922             break;
14923         case CMD_LEVEL_AGGRESSION:
14924             // Aggression can be set per spawn.
14925             next.aggression = GET_INT_ARG(1);
14926             break;
14927         case CMD_LEVEL_CREDIT:
14928             next.credit = GET_INT_ARG(1);
14929             break;
14930         case CMD_LEVEL_ITEMTRANS:
14931         case CMD_LEVEL_ITEMALPHA:
14932             next.itemtrans = GET_INT_ARG(1);
14933             break;
14934         case CMD_LEVEL_COORDS:
14935             next.position.x = GET_FLOAT_ARG(1);
14936             next.position.z = GET_FLOAT_ARG(2);
14937             next.position.y = GET_FLOAT_ARG(3);
14938             break;
14939         case CMD_LEVEL_SPAWNSCRIPT:
14940             pos += lcmHandleCommandScripts(&arglist, buf + pos, &next.spawnscript, "Level spawn entry script", filename, 0, 1);
14941             break;
14942         case CMD_LEVEL_AT_SCRIPT:
14943             if(!Script_IsInitialized(&next.spawnscript))
14944             {
14945                 Script_Init(&next.spawnscript, "Level spawn entry script", filename, 1);
14946             }
14947             fetchInlineScript(buf, &scriptbuf, &pos, &sblen);
14948             if(!Script_AppendText(&next.spawnscript, scriptbuf, filename))
14949             {
14950                 errormessage = "Unable to parse level spawn entry script.\n";
14951                 goto lCleanup;
14952             }
14953             free(scriptbuf);
14954             scriptbuf = NULL;
14955             break;
14956         case CMD_LEVEL_AT:
14957             // Place entry on queue
14958             next.at = GET_INT_ARG(1);
14959 
14960             if(Script_IsInitialized(&next.spawnscript))
14961             {
14962                 Script_Compile(&next.spawnscript);
14963             }
14964 
14965             __realloc(level->spawnpoints, level->numspawns);
14966             memcpy(&level->spawnpoints[level->numspawns], &next, sizeof(next));
14967             level->numspawns++;
14968 
14969             // And clear...
14970             memset(&next, 0, sizeof(next));
14971             break;
14972         default:
14973             if(command && command[0])
14974             {
14975                 if(!handle_txt_include(command, &arglist, &filename, fnbuf, &buf, &pos, &size))
14976                 {
14977                     printf("Command '%s' not understood in file '%s'!\n", command, filename);
14978                 }
14979             }
14980         }
14981 
14982         // Go to next line
14983         pos += getNewLineStart(buf + pos);
14984 
14985         if(isLoadingScreenTypeBar(bgPosi.set) || isLoadingScreenTypeBg(bgPosi.set))
14986         {
14987             update_loading(&bgPosi, pos, size);
14988         }
14989         //update_loading(bgPosi[0]+videomodes.hShift, bgPosi[1]+videomodes.vShift, bgPosi[2], bgPosi[3]+videomodes.hShift, bgPosi[4]+videomodes.vShift, pos, size, bgPosi[5]);
14990         else
14991         {
14992             update_loading(&loadingbg[1], pos, size);
14993         }
14994     }
14995 
14996     if(level->numpanels < 1)
14997     {
14998         errormessage = "Level error: level has no panels";
14999         goto lCleanup;
15000     }
15001 
15002     if(bgPath[0])
15003     {
15004         clearscreen(vscreen);
15005         spriteq_clear();
15006         load_background(bgPath, 1);
15007     }
15008     else if(background)
15009     {
15010         unload_background();
15011     }
15012 
15013     if(level->numlayers)
15014     {
15015 
15016         for(i = 0; i < level->numlayers; i++)
15017         {
15018             bgl = &(level->layers[i]);
15019             switch(bgl->oldtype)
15020             {
15021             case BGT_WATER: // default water hack
15022                 bgl->offset.z = background ? background->height : level->layers[0].size.y;
15023                 dm = &(bgl->drawmethod);
15024                 if(level->rocking)
15025                 {
15026                     dm->water.watermode = 3;
15027                     dm->water.beginsize = 1.0;
15028                     dm->water.endsize = 1 + bgl->size.y / 11.0;
15029                     dm->water.perspective = 0;
15030                     bgl->bgspeedratio = 2;
15031                 }
15032                 break;
15033             case BGT_PANEL:
15034                 panel_width = bgl->size.x;
15035                 panel_height = bgl->size.y;
15036             case BGT_FRONTPANEL:
15037                 if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN))
15038                 {
15039                     bgl->ratio.z = 1;
15040                 }
15041                 break;
15042             case BGT_BACKGROUND:
15043                 bgl->gfx.screen = background;
15044                 bgl->size.x = background->width;
15045                 bgl->size.y = background->height;
15046                 level->background = bgl;
15047                 break;
15048             default:
15049                 break;
15050             }
15051             load_layer(NULL, NULL, i);
15052         }
15053 
15054         if(level->background)
15055         {
15056             __realloc(level->layersref, level->numlayersref);
15057             level->layersref[level->numlayersref] = *(level->background);
15058             level->background = (s_layer *)(size_t)level->numlayersref++;
15059         }
15060 
15061 
15062         // non-panel type layers
15063         for(i = 0; i < level->numlayers; i++)
15064         {
15065             bgl = &(level->layers[i]);
15066             if(bgl->oldtype != BGT_PANEL && bgl->oldtype != BGT_BACKGROUND)
15067             {
15068                 __realloc(level->layersref, level->numlayersref);
15069                 level->layersref[level->numlayersref] = *bgl;
15070                 bgl = &(level->layersref[level->numlayersref]);
15071                 switch(bgl->oldtype)
15072                 {
15073                 case BGT_BGLAYER:
15074                     __realloc(level->bglayers, level->numbglayers);
15075                     level->bglayers[level->numbglayers++] = (s_layer *)(size_t)level->numlayersref;
15076                     break;
15077                 case BGT_FGLAYER:
15078                     __realloc(level->fglayers, level->numfglayers);
15079                     level->fglayers[level->numfglayers++] = (s_layer *)(size_t)level->numlayersref;
15080                     break;
15081                 case BGT_WATER:
15082                     __realloc(level->waters, level->numwaters);
15083                     level->waters[level->numwaters++] = (s_layer *)(size_t)level->numlayersref;
15084                     break;
15085                 case BGT_GENERIC:
15086                     __realloc(level->genericlayers, level->numgenericlayers);
15087                     level->genericlayers[level->numgenericlayers++] = (s_layer *)(size_t)level->numlayersref;
15088                     break;
15089                 case BGT_FRONTPANEL:
15090                     bgl->offset.x = level->numfrontpanels * bgl->size.x;
15091                     bgl->spacing.x = (frontpanels_loaded - 1) * bgl->size.x;
15092                     __realloc(level->frontpanels, level->numfrontpanels);
15093                     level->frontpanels[level->numfrontpanels++] = (s_layer *)(size_t)level->numlayersref;
15094                     break;
15095                 default:
15096                     break;
15097                 }
15098                 level->numlayersref++;
15099             }
15100         }
15101 
15102         //panels, normal neon screen
15103         for(i = 0; i < level->numpanels; i++)
15104         {
15105             for(j = 0; j < 3; j++)
15106             {
15107                 if(panels[order[i]][j])
15108                 {
15109                     __realloc(level->layersref, level->numlayersref);
15110                     level->layersref[level->numlayersref] = level->layers[panels[order[i]][j]];
15111                     bgl = &(level->layersref[level->numlayersref]);
15112                     bgl->offset.x = panel_width * i;
15113                     level->panels[i][j] = (s_layer *)(size_t)level->numlayersref;
15114                     level->numlayersref++;
15115                 }
15116             }
15117         }
15118 
15119         //fix realloc junk pointers
15120         bgl = level->layersref;
15121         level->background = bgl + (size_t)level->background;
15122         for(i = 0; i < level->numpanels; i++)
15123             for(j = 0; j < 3; j++)
15124             {
15125                 level->panels[i][j] = bgl + (size_t)level->panels[i][j];
15126             }
15127         for(i = 0; i < level->numfrontpanels; i++)
15128         {
15129             level->frontpanels[i] = bgl + (size_t)level->frontpanels[i];
15130         }
15131         for(i = 0; i < level->numbglayers; i++)
15132         {
15133             level->bglayers[i] = bgl + (size_t)level->bglayers[i];
15134         }
15135         for(i = 0; i < level->numfglayers; i++)
15136         {
15137             level->fglayers[i] = bgl + (size_t)level->fglayers[i];
15138         }
15139         for(i = 0; i < level->numgenericlayers; i++)
15140         {
15141             level->genericlayers[i] = bgl + (size_t)level->genericlayers[i];
15142         }
15143         for(i = 0; i < level->numwaters; i++)
15144         {
15145             level->waters[i] = bgl + (size_t)level->waters[i];
15146         }
15147 
15148     }
15149 
15150     if(musicPath[0])
15151     {
15152         music(musicPath, 1, musicOffset);
15153     }
15154 
15155     timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
15156     level->width = level->numpanels * panel_width;
15157 
15158     if(level->width < videomodes.hRes)
15159     {
15160         level->width = videomodes.hRes;
15161     }
15162 
15163     scrollmaxx = level->width - videomodes.hRes;
15164     scrollmaxz = panel_height;
15165 
15166     if(level->scrolldir & SCROLL_LEFT)
15167     {
15168         advancex = (float)(level->width - videomodes.hRes);
15169     }
15170     else if(level->scrolldir & SCROLL_INWARD)
15171     {
15172         advancey = (float)(panel_height - videomodes.vRes);
15173     }
15174 
15175     if(exit_blocked)
15176     {
15177         addwall(level->width - 30, PLAYER_MAX_Z, -panel_height, 0, 1000, 1000, panel_height, MAX_WALL_HEIGHT + 1, 0);
15178     }
15179     if(exit_hole)
15180     {
15181         addhole(level->width, PLAYER_MAX_Z, -panel_height, 0, 1000, 1000, panel_height, 0, 0);
15182     }
15183 
15184     if(crlf)
15185     {
15186         printf("\n");
15187     }
15188     printf("Level Loaded:    '%s'\n", level->name);
15189     totalram = getSystemRam(BYTES);
15190     freeram = getFreeRam(BYTES);
15191     usedram = getUsedRam(BYTES);
15192     printf("Total Ram: %"PRIu64" Bytes\n Free Ram: %"PRIu64" Bytes\n Used Ram: %"PRIu64" Bytes\n", totalram, freeram, usedram);
15193     printf("Total sprites mapped: %d\n\n", sprites_loaded);
15194 
15195 lCleanup:
15196 
15197     if (panels)
15198     {
15199         free(panels);
15200     }
15201     if (order)
15202     {
15203         free(order);
15204     }
15205 
15206     if(buf)
15207     {
15208         free(buf);
15209     }
15210     if(scriptbuf)
15211     {
15212         free(scriptbuf);
15213     }
15214 
15215     if(errormessage)
15216     {
15217         shutdown(1, "ERROR: load_level, file %s, line %d, message: %s", filename, line, errormessage);
15218     }
15219 }
15220 
15221 
15222 
15223 
15224 
15225 /////////////////////////////////////////////////////////////////////////////
15226 //  Status                                                                  //
15227 /////////////////////////////////////////////////////////////////////////////
bar(int x,int y,int value,int maxvalue,s_barstatus * pstatus)15228 void bar(int x, int y, int value, int maxvalue, s_barstatus *pstatus)
15229 {
15230     int max = 100, len, alphabg = 0, bgindex, colourindex;
15231     int forex, forey, forew, foreh, bkw, bkh;
15232     s_drawmethod dm = plainmethod;
15233 
15234     x += pstatus->offset.x;
15235     y += pstatus->offset.y;
15236 
15237     if(pstatus->orientation == HORIZONTALBAR)
15238     {
15239         max = pstatus->size.x;
15240     }
15241     else if(pstatus->orientation == VERTICALBAR)
15242     {
15243         max = pstatus->size.y;
15244     }
15245     else
15246     {
15247         return;
15248     }
15249 
15250     if (value < 0)
15251     {
15252         value = 0;
15253     }
15254 
15255     if (value > maxvalue)
15256     {
15257         value = maxvalue;
15258     }
15259 
15260     if(pstatus->type == VALUEBAR)
15261     {
15262         if(max > maxvalue)
15263         {
15264             max = maxvalue;
15265         }
15266         if(colorbars)
15267         {
15268             if(value <= max / 4)
15269             {
15270                 bgindex = 0;
15271                 colourindex = 1;
15272             }
15273             else if(value <= max / 2)
15274             {
15275                 bgindex = 0;
15276                 colourindex = 2;
15277             }
15278             else if(value <= max)
15279             {
15280                 bgindex = 0;
15281                 colourindex = 3;
15282             }
15283             else
15284             {
15285                 colourindex = value / (max + 1) + 3;
15286                 bgindex = colourindex - 1;
15287             }
15288             if(colourindex > 10)
15289             {
15290                 colourindex = bgindex = 10;
15291             }
15292         }
15293         else
15294         {
15295             colourindex = 2;
15296             bgindex = value > max ? 5 : 1;
15297         }
15298 
15299         len = value % max;
15300         if(!len && value)
15301         {
15302             len = max;
15303         }
15304         alphabg = value > max ? 0 : (BLEND_MULTIPLY + 1);
15305     }
15306     else if(pstatus->type == PERCENTAGEBAR)
15307     {
15308         colourindex = colorbars ? (value * 5 / maxvalue + 1) : 2;
15309         bgindex = colorbars ? 8 : 1;
15310         len = value * max / maxvalue;
15311         if(!len && value)
15312         {
15313             len = 1;
15314         }
15315         alphabg = BLEND_MULTIPLY + 1;
15316     }
15317     else
15318     {
15319         return;
15320     }
15321 
15322     if(pstatus->orientation == HORIZONTALBAR)
15323     {
15324         forex = pstatus->direction == BARSTATUS_DIR_INVERT ? (x + max - len) : x;
15325         forey = y;
15326         forew = len;
15327         bkw = max;
15328         bkh = foreh = pstatus->size.y;
15329     }
15330     else if(pstatus->orientation == VERTICALBAR)
15331     {
15332         forex = x;
15333         forey = pstatus->direction == BARSTATUS_DIR_INVERT ? y : (y + max - len);
15334         bkw = forew = pstatus->size.x;
15335         foreh = len;
15336         bkh = max;
15337     }
15338     else
15339     {
15340         return;
15341     }
15342 
15343     if(!pstatus->colourtable)
15344     {
15345         pstatus->colourtable = &hpcolourtable;
15346     }
15347 
15348     dm.alpha = alphabg;
15349     spriteq_add_box(x + 1, y + 1, bkw, bkh, HUD_Z + 1 + pstatus->backlayer, (*pstatus->colourtable)[bgindex], &dm);
15350     spriteq_add_box(forex + 1, forey + 1, forew, foreh, HUD_Z + 2 + pstatus->barlayer, (*pstatus->colourtable)[colourindex], NULL);
15351 
15352     if(pstatus->noborder == 0)
15353     {
15354         spriteq_add_line(x, y, x + bkw + 1, y, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Top border.
15355         spriteq_add_line(x, y + bkh + 1, x + bkw + 1, y + bkh + 1, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Bottom border.
15356         spriteq_add_line(x, y + 1, x, y + bkh, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Left border.
15357         spriteq_add_line(x + bkw + 1, y + 1, x + bkw + 1, y + bkh, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Right border.
15358         spriteq_add_line(x, y + bkh + 2, x + bkw + 1, y + bkh + 2, HUD_Z + pstatus->borderlayer, color_black, NULL); //Bottom shadow.
15359         spriteq_add_line(x + bkw + 2, y + 1, x + bkw + 2, y + bkh + 2, HUD_Z + pstatus->borderlayer, color_black, NULL); //Right shadow.
15360     }
15361 }
15362 
goto_mainmenu(int flag)15363 void goto_mainmenu(int flag)
15364 {
15365     goto_mainmenu_flag = 1|(flag<<1);
15366 }
15367 
backto_mainmenu()15368 void static backto_mainmenu()
15369 {
15370     int i = 0;
15371     s_screen *pausebuffer = allocscreen(videomodes.hRes, videomodes.vRes, screenformat);
15372 
15373     copyscreen(pausebuffer, vscreen);
15374     spriteq_draw(pausebuffer, 0, MIN_INT, MAX_INT, 0, 0);
15375     spriteq_clear();
15376     spriteq_add_screen(0, 0, MIN_INT, pausebuffer, NULL, 0);
15377     spriteq_lock();
15378 
15379     sound_pause_music(1);
15380     sound_pause_sample(1);
15381     pause = 2;
15382 
15383     if ( (goto_mainmenu_flag&1) ) goto_mainmenu_flag -= 1;
15384 
15385     update(1, 0);
15386 
15387     for(i = 0; i < MAX_PLAYERS; i++)
15388     {
15389         player[i].lives = 0;
15390     }
15391     endgame = 2;
15392     //sound_pause_music(0);
15393     //sound_pause_sample(0);
15394 
15395     pause = 0;
15396     bothnewkeys = 0;
15397     spriteq_unlock();
15398     spriteq_clear();
15399     freescreen(&pausebuffer);
15400 
15401     return;
15402 }
15403 
pausemenu()15404 void pausemenu()
15405 {
15406     int pauselector = 0;
15407     int quit = 0;
15408     int controlp = 0, i;
15409     int newkeys;
15410     s_set_entry *set = levelsets + current_set;
15411     s_screen *pausebuffer = allocscreen(videomodes.hRes, videomodes.vRes, screenformat);
15412 
15413     copyscreen(pausebuffer, vscreen);
15414     spriteq_draw(pausebuffer, 0, MIN_INT, MAX_INT, 0, 0);
15415     spriteq_clear();
15416     spriteq_add_screen(0, 0, MIN_INT, pausebuffer, NULL, 0);
15417     spriteq_lock();
15418 
15419     for(i = 0; i < set->maxplayers; i++)
15420     {
15421         if(player[i].ent && (player[i].newkeys & FLAG_START))
15422         {
15423             controlp = i;
15424             break;
15425         }
15426     }
15427 
15428     pause = 2;
15429     bothnewkeys = 0;
15430     while(!quit)
15431     {
15432         _menutextmshift(pauseoffset[4], -2, 0, pauseoffset[5], pauseoffset[6], Tr("Pause"));
15433         _menutextmshift((pauselector == 0)?pauseoffset[1]:pauseoffset[0], -1, 0, pauseoffset[2], pauseoffset[3], Tr("Continue"));
15434         _menutextmshift((pauselector == 1)?pauseoffset[1]:pauseoffset[0],  0, 0, pauseoffset[2], pauseoffset[3], Tr("End Game"));
15435 
15436         update(1, 0);
15437 
15438         newkeys = player[controlp].newkeys;
15439 
15440         if(newkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN))
15441         {
15442             pauselector ^= 1;
15443             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
15444         }
15445         if(newkeys & FLAG_START)
15446         {
15447             if(pauselector)
15448             {
15449                 for(i = 0; i < MAX_PLAYERS; i++)
15450                 {
15451                     player[i].lives = 0;
15452                 }
15453                 endgame = 2;
15454             }
15455             quit = 1;
15456             sound_pause_music(0);
15457             sound_pause_sample(0);
15458             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
15459             pauselector = 0;
15460         }
15461         if(newkeys & FLAG_ESC)
15462         {
15463             quit = 1;
15464             sound_pause_music(0);
15465             sound_pause_sample(0);
15466             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
15467             pauselector = 0;
15468         }
15469         if(newkeys & FLAG_SCREENSHOT)
15470         {
15471             pause = 1;
15472             sound_pause_music(1);
15473             sound_pause_sample(1);
15474             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
15475             menu_options();
15476         }
15477     }
15478     pause = 0;
15479     bothnewkeys = 0;
15480     spriteq_unlock();
15481     spriteq_clear();
15482     freescreen(&pausebuffer);
15483 }
15484 
getFPS(void)15485 unsigned getFPS(void)
15486 {
15487     static unsigned lasttick = 0, framerate = 0;
15488     unsigned curtick = timer_gettick();
15489     if(lasttick > curtick)
15490     {
15491         lasttick = curtick;
15492     }
15493     framerate = (framerate + (curtick - lasttick)) / 2;
15494     lasttick = curtick;
15495     if(!framerate)
15496     {
15497         return 0;
15498     }
15499 #ifdef PSP
15500     return ((10000000 / framerate) + 9) / 10;
15501 #else
15502     return ((10000000 / framerate) + 9) / 10000;
15503 #endif
15504 }
15505 
updatestatus()15506 void updatestatus()
15507 {
15508 
15509     int dt;
15510     int i;
15511     s_model *model = NULL;
15512     s_set_entry *set = levelsets + current_set;
15513 
15514     for(i = 0; i < set->maxplayers; i++)
15515     {
15516         if(player[i].ent)
15517         {
15518             ;
15519         }
15520         else if(player[i].joining && player[i].name[0])
15521         {
15522             model = findmodel(player[i].name);
15523             if((player[i].playkeys & FLAG_ANYBUTTON || skipselect[i][0]) && !freezeall && !nojoin)    // Can't join while animations are frozen
15524             {
15525                 player[i].lives = PLAYER_LIVES;            // to address new lives settings
15526                 player[i].joining = 0;
15527                 player[i].hasplayed = 1;
15528                 player[i].spawnhealth = model->health;
15529                 player[i].spawnmp = model->mp;
15530 
15531                 spawnplayer(i);
15532 
15533                 execute_join_script(i);
15534 
15535                 player[i].disablekeys = player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0;
15536 
15537                 if(!nodropen)
15538                 {
15539                     drop_all_enemies();    //27-12-2004  If drop enemies is on, drop all enemies
15540                 }
15541 
15542                 if(!level->noreset)
15543                 {
15544                     timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
15545                 }
15546 
15547             }
15548             else if(player[i].playkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT))
15549             {
15550                 model = ((player[i].playkeys & FLAG_MOVELEFT) ? prevplayermodeln : nextplayermodeln)(model, i);
15551                 strcpy(player[i].name, model->name);
15552 
15553                 //player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmap(model, i - 1) : 0;
15554                 // NOSAME X 2 // By White Dragon
15555                 player[i].colourmap = 0;
15556                 if ( colourselect && (set->nosame & 2) )
15557                 {
15558                     int nosamef = -1;
15559                     int ii = 0;
15560                     for(ii = 0; ii < set->maxplayers; ii++)
15561                     {
15562                         if ( i != ii && stricmp(player[i].name,player[ii].name) == 0 )
15563                         {
15564                             if ( player[i].colourmap == player[ii].colourmap  )
15565                             {
15566                                 if ( nosamef == player[ii].colourmap ) break; // avoid infinite loop
15567                                 player[i].colourmap = nextcolourmap(model,player[i].colourmap);
15568                                 ii = 0;
15569                                 if ( nosamef == -1 ) nosamef = player[ii].colourmap;
15570                                 continue;
15571                             }
15572                         } else continue;
15573                     }
15574                 }
15575                 // NOSAME X 2
15576 
15577                 player[i].playkeys = 0;
15578             }
15579             // don't like a characters color try a new one!
15580             else if(player[i].playkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN) && colourselect)
15581             {
15582                 player[i].colourmap = ((player[i].playkeys & FLAG_MOVEUP) ? nextcolourmap : prevcolourmap)(model, player[i].colourmap);
15583                 // NOSAME X 2 // By White Dragon
15584                 if ( colourselect && (set->nosame & 2) )
15585                 {
15586                     int nosamef = -1;
15587                     int ii = 0;
15588                     for(ii = 0; ii < set->maxplayers; ii++)
15589                     {
15590                         if ( i != ii && stricmp(player[i].name,player[ii].name) == 0 )
15591                         {
15592                             if ( player[i].colourmap == player[ii].colourmap  )
15593                             {
15594                                 if ( nosamef == player[ii].colourmap ) break; // avoid infinite loop
15595                                 player[i].colourmap = ((player[i].playkeys & FLAG_MOVEUP) ? nextcolourmap : prevcolourmap)(model, player[i].colourmap);
15596                                 ii = 0;
15597                                 if ( nosamef == -1 ) nosamef = player[ii].colourmap;
15598                                 continue;
15599                             }
15600                         } else continue;
15601                     }
15602                 }
15603                 // NOSAME X 2
15604                 player[i].playkeys = 0;
15605             }
15606         }
15607         else if( !nojoin && (player[i].credits || credits || (!player[i].hasplayed && noshare)) )
15608         {
15609             if(player[i].playkeys & FLAG_START)
15610             {
15611                 player[i].lives = 0;
15612                 model = skipselect[i][0] ? findmodel(skipselect[i]) : nextplayermodeln(NULL, i);
15613                 strncpy(player[i].name, model->name, MAX_NAME_LEN);
15614 
15615                 //player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmap(model, i - 1) : 0;
15616                 // NOSAME X 2 // By White Dragon
15617                 player[i].colourmap = 0;
15618                 if ( colourselect && (set->nosame & 2) )
15619                 {
15620                     int nosamef = -1;
15621                     int ii = 0;
15622                     for(ii = 0; ii < set->maxplayers; ii++)
15623                     {
15624                         if ( i != ii && stricmp(player[i].name,player[ii].name) == 0 )
15625                         {
15626                             if ( player[i].colourmap == player[ii].colourmap  )
15627                             {
15628                                 if ( nosamef == player[ii].colourmap ) break; // avoid infinite loop
15629                                 player[i].colourmap = nextcolourmap(model,player[i].colourmap);
15630                                 ii = 0;
15631                                 if ( nosamef == -1 ) nosamef = player[ii].colourmap;
15632                                 continue;
15633                             }
15634                         } else continue;
15635                     }
15636                 }
15637                 // NOSAME X 2
15638 
15639                 player[i].joining = 1;
15640                 player[i].disablekeys = player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0;
15641 
15642                 if(!level->noreset)
15643                 {
15644                     timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
15645                 }
15646 
15647                 if(!player[i].hasplayed && noshare)
15648                 {
15649                     player[i].credits = CONTINUES;
15650                 }
15651 
15652                 if(!creditscheat)
15653                 {
15654                     if(noshare)
15655                     {
15656                         --player[i].credits;
15657                     }
15658                     else
15659                     {
15660                         --credits;
15661                     }
15662                     if(set->continuescore == 1)
15663                     {
15664                         player[i].score = 0;
15665                     }
15666                     if(set->continuescore == 2)
15667                     {
15668                         player[i].score = player[i].score + 1;
15669                     }
15670                 }
15671             }
15672         }
15673     }// end of for
15674 
15675     dt = timeleft / COUNTER_SPEED;
15676     if(dt >= 99)
15677     {
15678         dt      = 99;
15679 
15680         oldtime = 99;
15681     }
15682     if(dt <= 0)
15683     {
15684         dt      = 0;
15685         oldtime = 99;
15686     }
15687 
15688     if(dt < oldtime || oldtime == 0)
15689     {
15690         execute_timetick_script(dt, go_time);
15691         oldtime = dt;
15692     }
15693 
15694     timetoshow = dt;
15695 
15696     if(dt < 99)
15697     {
15698         showtimeover = 0;
15699     }
15700 
15701     if(go_time > time)
15702     {
15703         dt = (go_time - time) % GAME_SPEED;
15704 
15705         if(dt < GAME_SPEED / 2)
15706         {
15707             showgo = 1;
15708             if(gosound == 0 )
15709             {
15710 
15711                 if(SAMPLE_GO >= 0)
15712                 {
15713                     sound_play_sample(SAMPLE_GO, 0, savedata.effectvol, savedata.effectvol, 100);    // 26-12-2004 Play go sample as arrow flashes
15714                 }
15715 
15716                 gosound = 1;                // 26-12-2004 Sets sample as already played - stops sample repeating too much
15717             }
15718         }
15719         else
15720         {
15721             showgo = gosound = 0;    //26-12-2004 Resets go sample after loop so it can be played again next time
15722         }
15723     }
15724     else
15725     {
15726         showgo = 0;
15727     }
15728 
15729 }
15730 
15731 
15732 // Caskey, Damon V.
15733 // 2016-11-16
15734 //
15735 // Draw dot onto screen to indicate actual entity position,
15736 // with text readout of Base, X, Y, and Z coordinates directly below.
draw_position_entity(entity * entity,int offset_z,int color,s_drawmethod * drawmethod)15737 void draw_position_entity(entity *entity, int offset_z, int color, s_drawmethod *drawmethod)
15738 {
15739     #define FONT                0
15740     #define TEXT_MARGIN_Y       1
15741     #define OFFSET_LAYER       -2
15742 
15743     // Position array keys
15744     // and size constants.
15745     enum
15746     {
15747         KEY_BASE,
15748         KEY_X,
15749         KEY_Y,
15750         KEY_Z,
15751         POS_ARRAY_SIZE
15752     };
15753 
15754     typedef struct
15755     {
15756         s_axis_i    position;
15757         s_axis_i_2d size;
15758     } draw_coords;
15759 
15760     s_axis_i_2d screen_offset;          // Base location calculated from screen offsets.
15761     s_axis_i    base_pos;               // Entity position with screen offsets applied.
15762     draw_coords box;                    // On screen coords for display elements.
15763 
15764     int pos_value[POS_ARRAY_SIZE];      // Entity position for display - truncated to int.
15765     int i;                              // Counter.
15766     int str_offset_x;                   // Calculated offset of text for centering.
15767     int str_width_max;                  // largest string width.
15768     int str_height_max;                 // Largest string height.
15769     size_t str_size;                    // Memory size of string.
15770 
15771     const char  *pos_label[POS_ARRAY_SIZE];  // Labels for string position values.
15772     char        *pos_final[POS_ARRAY_SIZE];  // Final string to display position.
15773 
15774     // Initialize box.
15775     box.position.x = 0;
15776     box.position.y = 0;
15777     box.position.z = 0;
15778 
15779     // Populate position labels.
15780     pos_label[KEY_BASE]          = "B: %d";
15781     pos_label[KEY_X]             = "X: %d";
15782     pos_label[KEY_Y]             = "Y: %d";
15783     pos_label[KEY_Z]             = "Z: %d";
15784 
15785     // Populate position values - truncated to int.
15786     pos_value[KEY_BASE]         = (int)entity->base;
15787     pos_value[KEY_X]            = (int)entity->position.x;
15788     pos_value[KEY_Y]            = (int)entity->position.y;
15789     pos_value[KEY_Z]            = (int)entity->position.z;
15790 
15791     // Allocate memory and create finished strings.
15792     for(i = 0; i < POS_ARRAY_SIZE; i++)
15793     {
15794         // Get the total memory size we will need.
15795         str_size  = sizeof(char) * (strlen(pos_label[i]) + 1);
15796         str_size += sizeof(char) * (sizeof(pos_value[i]) + 1);
15797 
15798         // Allocate memory.
15799         pos_final[i] = malloc(str_size);
15800 
15801         // If allocation was successful, concatenate
15802         // position label and position value.
15803         if(pos_final[i])
15804         {
15805             sprintf(pos_final[i], pos_label[i], pos_value[i]);
15806         }
15807     }
15808 
15809     // Get the largest string X and Y space.
15810     str_width_max   = font_string_width_max(*pos_final, FONT);
15811     str_height_max  = fontheight(FONT);
15812 
15813     // Get our base offsets from screen vs. location.
15814     screen_offset.x = screenx - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
15815     screen_offset.y = screeny - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
15816 
15817     // Get entity position with screen offsets.
15818     base_pos.x = entity->position.x - screen_offset.x;
15819     base_pos.y = (entity->position.z - offset_z) - entity->position.y - screen_offset.y;
15820 
15821     // Get a value of half the text width.
15822     // We can use this to center our text
15823     // on the entity.
15824     str_offset_x = str_width_max / 2;
15825 
15826     // Apply text offset.
15827     box.position.x = base_pos.x - str_offset_x;
15828 
15829     box.position.y = base_pos.y;
15830     box.position.z = entity->position.z + offset_z;
15831 
15832     // Draw position dot.
15833     // The +1 to Z is a quick fix - offset_z
15834     // distorts the dot's vertical position
15835     // instead of just adjusting Z.
15836     spriteq_add_dot(base_pos.x, base_pos.y, box.position.z+1, color, drawmethod);
15837 
15838     // Print each position text.
15839     for(i = 0; i < POS_ARRAY_SIZE; i++)
15840     {
15841         // If the position string exists then
15842         // we can find a position, print it to
15843         // the screen, and free up allocated memory.
15844         if(pos_final[i])
15845         {
15846            // Add font height and margin to Y position.
15847             base_pos.y += (str_height_max + TEXT_MARGIN_Y);
15848 
15849             // Print position text.
15850             font_printf(box.position.x, base_pos.y, FONT, OFFSET_LAYER, pos_final[i]);
15851 
15852             // Release memory allocated for the string.
15853             free(pos_final[i]);
15854         }
15855     }
15856 
15857     return;
15858 
15859     // Remove local constants.
15860     #undef FONT
15861     #undef TEXT_MARGIN_Y
15862     #undef OFFSET_LAYER
15863 }
15864 
15865 // White Dragon
15866 // 2016-11-28
15867 //
15868 // Draw entity features
draw_features_entity(entity * entity,int offset_z,int color,s_drawmethod * drawmethod)15869 void draw_features_entity(entity *entity, int offset_z, int color, s_drawmethod *drawmethod)
15870 {
15871     #define FONT                0
15872     #define TEXT_MARGIN_Y       1
15873     #define OFFSET_LAYER       -2
15874 
15875     // Features array keys
15876     // and size constants.
15877     enum
15878     {
15879         KEY_MODELNAME,
15880         CHAR_ARRAY_SIZE
15881     };
15882 
15883     typedef struct
15884     {
15885         s_axis_i    position;
15886         s_axis_i_2d size;
15887     } draw_coords;
15888 
15889     s_axis_i_2d screen_offset;          // Base location calculated from screen offsets.
15890     s_axis_i    base_pos;               // Entity position with screen offsets applied.
15891     draw_coords box;                    // On screen coords for display elements.
15892 
15893     char *char_value[CHAR_ARRAY_SIZE];  // Entity features for display
15894     int i;                              // Counter.
15895     int str_offset_x;                   // Calculated offset of text for centering.
15896     int str_width_max;                  // largest string width.
15897     int str_height_max;                 // Largest string height.
15898     size_t str_size;                    // Memory size of string.
15899 
15900     const char  *char_label[CHAR_ARRAY_SIZE];  // Labels for string features
15901     char        *char_final[CHAR_ARRAY_SIZE];  // Final string to display.
15902 
15903     // Initialize box.
15904     box.position.x = 0;
15905     box.position.y = 0;
15906     box.position.z = 0;
15907 
15908     // Populate position labels.
15909     char_label[KEY_MODELNAME]    = "%s";
15910 
15911     // Populate position values - truncated to int.
15912     char_value[KEY_MODELNAME] = malloc( sizeof(char) * (strlen(entity->model->name)+1) );
15913     memcpy( char_value[KEY_MODELNAME], entity->model->name, (strlen(entity->model->name)+1) );
15914 
15915     // Allocate memory and create finished strings.
15916     for(i = 0; i < CHAR_ARRAY_SIZE; i++)
15917     {
15918         // Get the total memory size we will need.
15919         str_size  = sizeof(char) * (strlen(char_label[i]) + 1);
15920         str_size += sizeof(char) * (strlen(char_value[i]) + 1);
15921 
15922         // Allocate memory.
15923         char_final[i] = malloc(str_size);
15924 
15925         // If allocation was successful, concatenate
15926         // position label and position value.
15927         if(char_final[i])
15928         {
15929             sprintf(char_final[i], char_label[i], char_value[i]);
15930         }
15931 
15932         free(char_value[i]);
15933     }
15934 
15935     // Get the largest string X and Y space.
15936     str_width_max   = font_string_width_max(*char_final, FONT);
15937     str_height_max  = fontheight(FONT);
15938 
15939     // Get our base offsets from screen vs. location.
15940     screen_offset.x = screenx - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
15941     screen_offset.y = screeny - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
15942 
15943     // Get entity position with screen offsets.
15944     base_pos.x = entity->position.x - screen_offset.x;
15945     base_pos.y = (entity->position.z - offset_z) - entity->position.y - screen_offset.y;
15946 
15947     // Get a value of half the text width.
15948     // We can use this to center our text
15949     // on the entity.
15950     str_offset_x = (str_width_max - font_string_width(FONT, "0")) / 2;
15951 
15952     // Apply text offset.
15953     box.position.x = base_pos.x - str_offset_x;
15954 
15955     box.position.y = base_pos.y;
15956     box.position.z = entity->position.z + offset_z;
15957 
15958     // Draw position dot.
15959     // The +1 to Z is a quick fix - offset_z
15960     // distorts the dot's vertical position
15961     // instead of just adjusting Z.
15962     if (!savedata.debug_position) spriteq_add_dot(base_pos.x, base_pos.y, box.position.z+1, color, drawmethod);
15963 
15964     // Print each feature text.
15965     if (savedata.debug_position) base_pos.y += (str_height_max + TEXT_MARGIN_Y)*(3+1);
15966     for(i = 0; i < CHAR_ARRAY_SIZE; i++)
15967     {
15968         // If the position string exists then
15969         // we can find a position, print it to
15970         // the screen, and free up allocated memory.
15971         if(char_final[i])
15972         {
15973            // Add font height and margin to Y position.
15974             base_pos.y += (str_height_max + TEXT_MARGIN_Y);
15975 
15976             // Print position text.
15977             font_printf(box.position.x, base_pos.y, FONT, OFFSET_LAYER, char_final[i]);
15978 
15979             // Release memory allocated for the string.
15980             free(char_final[i]);
15981         }
15982     }
15983 
15984     return;
15985 
15986     // Remove local constants.
15987     #undef FONT
15988     #undef TEXT_MARGIN_Y
15989     #undef OFFSET_LAYER
15990 }
15991 
15992 // Caskey, Damon V.
15993 // 2016-11-16
15994 //
15995 // Convert entity's world position to screen
15996 // 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)15997 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)
15998 {
15999     s_axis_i_2d screen_offset;  // Base location calculated from screen offsets.
16000     s_axis_i    base_pos;       // Entity position with screen offsets applied.
16001 
16002     typedef struct
16003     {
16004         s_axis_i position;
16005         s_axis_i_2d size;
16006     } draw_coords;
16007 
16008     draw_coords box;
16009 
16010     // Get our base offsets from screen vs. location.
16011     screen_offset.x = screenx - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
16012     screen_offset.y = screeny - ((entity->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
16013 
16014     // Get entity position with screen offsets.
16015     base_pos.x = entity->position.x - screen_offset.x;
16016     base_pos.y = (entity->position.z - offset_z) - entity->position.y - screen_offset.y;
16017 
16018     // Now apply drawing coords to position.
16019     box.size.x = size_w - pos_x;
16020 
16021     // Don't forget to accommodate for
16022     // entity direction.
16023     if(entity->direction == DIRECTION_RIGHT)
16024     {
16025         box.position.x = base_pos.x + pos_x;
16026     }
16027     else
16028     {
16029         box.position.x = base_pos.x - (box.size.x + pos_x);
16030     }
16031 
16032     box.position.y = base_pos.y + pos_y;
16033     box.size.y = (base_pos.y + size_h) - box.position.y;
16034 
16035     box.position.z = pos_z + offset_z;
16036 
16037     // Add box to que.
16038     spriteq_add_box(box.position.x, box.position.y, box.size.x, box.size.y, box.position.z, color, drawmethod);
16039 }
16040 
draw_visual_debug()16041 void draw_visual_debug()
16042 {
16043     #define LOCAL_COLOR_BLUE  _makecolour(0, 0, 255)
16044     #define LOCAL_COLOR_GREEN _makecolour(0, 255, 0)
16045     #define LOCAL_COLOR_RED   _makecolour(255, 0, 255)
16046     #define LOCAL_COLOR_WHITE _makecolour(255, 255, 255)
16047 
16048     int i;
16049     int instance;
16050     s_hitbox            *coords;
16051     s_collision_attack  *collision_attack;
16052     s_collision_body    *collision_body;
16053     s_drawmethod        drawmethod = plainmethod;
16054     entity              *entity;
16055 
16056     drawmethod.alpha = 1;
16057 
16058     for(i=0; i<ent_max; i++)
16059     {
16060         entity = ent_list[i];
16061 
16062         // Entity must exist.
16063         if(!entity)
16064         {
16065             continue;
16066         }
16067 
16068         // Entity must be alive.
16069         if(entity->dead)
16070         {
16071             continue;
16072         }
16073 
16074         // Show offset and position.
16075         if(savedata.debug_position)
16076         {
16077             draw_position_entity(entity, 0, LOCAL_COLOR_WHITE, NULL);
16078         }
16079 
16080         // Show features.
16081         if(savedata.debug_features)
16082         {
16083             draw_features_entity(entity, 0, LOCAL_COLOR_WHITE, NULL);
16084         }
16085 
16086         // Collision body debug requested?
16087         if(savedata.debug_collision_range)
16088         {
16089             draw_box_on_entity(entity, entity->animation->range.min.x, entity->animation->range.min.y, entity->position.z+1, entity->animation->range.max.x, entity->animation->range.max.y, -1, LOCAL_COLOR_GREEN, &drawmethod);
16090         }
16091 
16092         // Collision body debug requested?
16093         if(savedata.debug_collision_body)
16094         {
16095             // Animation has collision?
16096             if(entity->animation->collision_body)
16097             {
16098                 // Frame has collision?
16099                 if(entity->animation->collision_body[entity->animpos])
16100                 {
16101                     // Loop instances of collision.
16102                     for(instance = 0; instance < max_collisons; instance++)
16103                     {
16104                         // Get collision instance pointer.
16105                         collision_body = entity->animation->collision_body[entity->animpos]->instance[instance];
16106 
16107                         // Valid collision instance pointer found?
16108                         if(collision_body)
16109                         {
16110                             coords = collision_body->coords;
16111                             draw_box_on_entity(entity, coords->x, coords->y, entity->position.z+1, coords->width, coords->height, 2, LOCAL_COLOR_BLUE, &drawmethod);
16112                         }
16113                     }
16114                 }
16115             }
16116         }
16117 
16118         // Collision attack requested?
16119         if(savedata.debug_collision_attack)
16120         {
16121             // Animation has collision?
16122             if(entity->animation->collision_attack)
16123             {
16124                 // Frame has collision?
16125                 if(entity->animation->collision_attack[entity->animpos])
16126                 {
16127                     // Loop instances of collision.
16128                     for(instance = 0; instance < max_collisons; instance++)
16129                     {
16130                         // Get collision instance pointer.
16131                         collision_attack = entity->animation->collision_attack[entity->animpos]->instance[instance];
16132 
16133                         // Valid collision instance pointer found?
16134                         if(collision_attack)
16135                         {
16136                             coords = collision_attack->coords;
16137                             draw_box_on_entity(entity, coords->x, coords->y, entity->position.z+1, coords->width, coords->height, 2, LOCAL_COLOR_RED, &drawmethod);
16138                         }
16139                     }
16140                 }
16141             }
16142         }
16143     }
16144 
16145     #undef LOCAL_COLOR_BLUE
16146     #undef LOCAL_COLOR_GREEN
16147     #undef LOCAL_COLOR_RED
16148     #undef LOCAL_COLOR_WHITE
16149 }
16150 
16151 
predrawstatus()16152 void predrawstatus()
16153 {
16154 
16155     int icon = 0;
16156     int i;
16157     unsigned long tmp;
16158     s_set_entry *set = levelsets + current_set;
16159     s_model *model = NULL;
16160     s_drawmethod drawmethod = plainmethod;
16161 
16162     if(bgicon >= 0)
16163     {
16164         spriteq_add_sprite(videomodes.hShift + bgicon_offsets[0], savedata.windowpos + bgicon_offsets[1], bgicon_offsets[2], bgicon, NULL, 0);
16165     }
16166     if(olicon >= 0)
16167     {
16168         spriteq_add_sprite(videomodes.hShift + olicon_offsets[0], savedata.windowpos + olicon_offsets[1], olicon_offsets[2], olicon, NULL, 0);
16169     }
16170 
16171     for(i = 0; i < set->maxplayers; i++)
16172     {
16173         if(player[i].ent)
16174         {
16175             tmp = player[i].score; //work around issue on 64bit where sizeof(long) != sizeof(int)
16176             if(!pscore[i][2] && !pscore[i][3] && !pscore[i][4] && !pscore[i][5])
16177             {
16178                 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);
16179             }
16180             else
16181             {
16182                 font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1], pscore[i][6], 0, "%s", player[i].ent->name);
16183                 font_printf(videomodes.shiftpos[i] + pscore[i][2], savedata.windowpos + pscore[i][3], pscore[i][6], 0, "-");
16184                 font_printf(videomodes.shiftpos[i] + pscore[i][4], savedata.windowpos + pscore[i][5], pscore[i][6], 0, (scoreformat ? "%09lu" : "%lu"), tmp);
16185             }
16186 
16187             if(player[i].ent->health <= 0)
16188             {
16189                 icon = player[i].ent->modeldata.icon.die;
16190             }
16191             else if(player[i].ent->inpain)
16192             {
16193                 icon = player[i].ent->modeldata.icon.pain;
16194             }
16195             else if(player[i].ent->getting)
16196             {
16197                 icon = player[i].ent->modeldata.icon.get;
16198             }
16199             else
16200             {
16201                 icon = player[i].ent->modeldata.icon.def;
16202             }
16203 
16204             if(icon >= 0)
16205             {
16206                 drawmethod.table = player[i].ent->modeldata.icon.usemap ? player[i].ent->colourmap : NULL;
16207                 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], savedata.windowpos + picon[i][1], 10000, icon, &drawmethod, 0);
16208             }
16209 
16210             if(player[i].ent->weapent)
16211             {
16212                 if(player[i].ent->weapent->modeldata.icon.weapon >= 0)
16213                 {
16214                     drawmethod.table = player[i].ent->weapent->modeldata.icon.usemap ? player[i].ent->weapent->colourmap : NULL;
16215                     spriteq_add_sprite(videomodes.shiftpos[i] + piconw[i][0], savedata.windowpos + piconw[i][1], 10000, player[i].ent->weapent->modeldata.icon.weapon, &drawmethod, 0);
16216                 }
16217 
16218                 if(player[i].ent->weapent->modeldata.typeshot && player[i].ent->weapent->modeldata.shootnum)
16219                 {
16220                     font_printf(videomodes.shiftpos[i] + pshoot[i][0], savedata.windowpos + pshoot[i][1], pshoot[i][2], 0, "%u", player[i].ent->weapent->modeldata.shootnum);
16221                 }
16222             }
16223 
16224             if(player[i].ent->modeldata.mp)
16225             {
16226                 drawmethod.table = player[i].ent->modeldata.icon.usemap ? player[i].ent->colourmap : NULL;
16227                 if(player[i].ent->modeldata.icon.mphigh > 0 && (player[i].ent->oldmp >= (player[i].ent->modeldata.mp * .66)))
16228                 {
16229                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mphigh, &drawmethod, 0);
16230                 }
16231                 else if(player[i].ent->modeldata.icon.mpmed > 0 && (player[i].ent->oldmp >= (player[i].ent->modeldata.mp * .33) && player[i].ent->oldmp < (player[i].ent->modeldata.mp * .66)))
16232                 {
16233                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mpmed, &drawmethod, 0);
16234                 }
16235                 else if(player[i].ent->modeldata.icon.mplow > 0 && (player[i].ent->oldmp >= 0 && player[i].ent->oldmp < (player[i].ent->modeldata.mp * .33)))
16236                 {
16237                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mplow, &drawmethod, 0);
16238                 }
16239                 else if(player[i].ent->modeldata.icon.mphigh > 0 && player[i].ent->modeldata.icon.mpmed == -1 && player[i].ent->modeldata.icon.mplow == -1)
16240                 {
16241                     spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, player[i].ent->modeldata.icon.mphigh, &drawmethod, 0);
16242                 }
16243             }
16244 
16245             font_printf(videomodes.shiftpos[i] + plifeX[i][0], savedata.windowpos + plifeX[i][1], plifeX[i][2], 0, "x");
16246             font_printf(videomodes.shiftpos[i] + plifeN[i][0], savedata.windowpos + plifeN[i][1], plifeN[i][2], 0, "%i", player[i].lives);
16247 
16248             if(rush[0] && player[i].ent->rush.count.current > 1 && time <= player[i].ent->rush.time)
16249             {
16250                 font_printf(videomodes.shiftpos[i] + prush[i][0], prush[i][1], rush[2], 0, "%s", rush_names[0]);
16251                 font_printf(videomodes.shiftpos[i] + prush[i][2], prush[i][3], rush[3], 0, "%i", player[i].ent->rush.count.current);
16252 
16253                 if(rush[0] != 2)
16254                 {
16255                     font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s", rush_names[1]);
16256                     font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i", player[i].ent->rush.count.max);
16257                 }
16258             }
16259 
16260             if(rush[0] == 2)
16261             {
16262                 font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s", rush_names[1]);
16263                 font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i", player[i].ent->rush.count.max);
16264             }
16265 
16266             if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife)
16267             {
16268                 // Displays life unless overridden by flag
16269                 font_printf(videomodes.shiftpos[i] + ename[i][0], savedata.windowpos + ename[i][1], ename[i][2], 0, player[i].ent->opponent->name);
16270                 if(player[i].ent->opponent->health <= 0)
16271                 {
16272                     icon = player[i].ent->opponent->modeldata.icon.die;
16273                 }
16274                 else if(player[i].ent->opponent->inpain)
16275                 {
16276                     icon = player[i].ent->opponent->modeldata.icon.pain;
16277                 }
16278                 else if(player[i].ent->opponent->getting)
16279                 {
16280                     icon = player[i].ent->opponent->modeldata.icon.get;
16281                 }
16282                 else
16283                 {
16284                     icon = player[i].ent->opponent->modeldata.icon.def;
16285                 }
16286 
16287                 if(icon >= 0)
16288                 {
16289                     drawmethod.table = player[i].ent->opponent->modeldata.icon.usemap ? player[i].ent->opponent->colourmap : NULL;
16290                     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
16291                 }
16292             }
16293         }
16294         else if(player[i].joining && player[i].name[0])
16295         {
16296             model = findmodel(player[i].name);
16297             font_printf(videomodes.shiftpos[i] + pnameJ[i][0], savedata.windowpos + pnameJ[i][1], pnameJ[i][6], 0, player[i].name);
16298             if(nojoin)
16299             {
16300                 font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3], pnameJ[i][6], 0, Tr("Please Wait"));
16301             }
16302             else
16303             {
16304                 font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3], pnameJ[i][6], 0, Tr("Select Hero"));
16305             }
16306             icon = model->icon.def;
16307 
16308             if(icon >= 0)
16309             {
16310                 drawmethod.table = model->icon.usemap ? model_get_colourmap(model, player[i].colourmap) : NULL;
16311                 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], picon[i][1], 10000, icon, &drawmethod, 0);
16312             }
16313         }
16314         else if(player[i].credits || credits || (!player[i].hasplayed && noshare))
16315         {
16316             if(player[i].credits && (time / (GAME_SPEED * 2)) & 1)
16317             {
16318                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), player[i].credits);
16319             }
16320             else if(credits && (time / (GAME_SPEED * 2)) & 1)
16321             {
16322                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), credits);
16323             }
16324             else if(nojoin)
16325             {
16326                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Please Wait"));
16327             }
16328             else
16329             {
16330                 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Press Start"));
16331             }
16332 
16333         }
16334         else
16335         {
16336             font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("GAME OVER"));
16337         }
16338     }// end of for
16339 
16340     if(savedata.debug_position
16341        || savedata.debug_features
16342        || savedata.debug_collision_attack
16343        || savedata.debug_collision_body
16344        || savedata.debug_collision_range)
16345     {
16346         // Collision boxes
16347         draw_visual_debug();
16348     }
16349 
16350     if(timeicon >= 0)
16351     {
16352         spriteq_add_sprite(videomodes.hShift + timeicon_offsets[0], savedata.windowpos + timeicon_offsets[1], 10000, timeicon, NULL, 0);
16353     }
16354     if(!level->notime)
16355     {
16356         font_printf(videomodes.hShift + timeloc[0] + 2, savedata.windowpos + timeloc[1] + 2, timeloc[5], 0, "%02i", timetoshow);
16357     }
16358     if(showtimeover)
16359     {
16360         font_printf(videomodes.hShift + 113, videomodes.vShift + savedata.windowpos + 110, timeloc[5], 0, Tr("TIME OVER"));
16361     }
16362 
16363     if(showgo)
16364     {
16365         if(level->scrolldir & SCROLL_LEFT) //TODO: upward and downward go
16366         {
16367 
16368             if(golsprite >= 0)
16369             {
16370                 spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, golsprite, NULL, 0);    // new sprite for left direction
16371             }
16372             else
16373             {
16374                 drawmethod.table = 0;
16375                 drawmethod.flipx = 1;
16376                 spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, gosprite, &drawmethod, 0);
16377             }
16378         }
16379         else if(level->scrolldir & SCROLL_RIGHT)
16380         {
16381             spriteq_add_sprite(videomodes.hRes - 40, 60 + videomodes.vShift, 10000, gosprite, NULL, 0);
16382         }
16383     }
16384 
16385     // Performance info.
16386     if(savedata.debuginfo)
16387     {
16388         spriteq_add_box(0, videomodes.dOffset - 12, videomodes.hRes, videomodes.dOffset + 12, 0x0FFFFFFE, 0, NULL);
16389         font_printf(2,                   videomodes.dOffset - 10, 0, 0, Tr("FPS: %03d"), getFPS());
16390         font_printf(videomodes.hRes / 2, videomodes.dOffset - 10, 0, 0, Tr("Free Ram: %s KB"), commaprint(freeram / 1000));
16391         font_printf(2,                   videomodes.dOffset,    0, 0, Tr("Sprites: %d / %d"), spriteq_get_sprite_count(), spriteq_get_sprite_max());
16392         font_printf(videomodes.hRes / 2, videomodes.dOffset,    0, 0, Tr("Used Ram: %s KB"), commaprint(usedram / 1000));
16393     }
16394 }
16395 
16396 // draw boss status on screen
drawenemystatus(entity * ent)16397 void drawenemystatus(entity *ent)
16398 {
16399     s_drawmethod drawmethod;
16400     int icon;
16401 
16402     if(ent->modeldata.namex > -1000 && ent->modeldata.namey > -1000)
16403     {
16404         font_printf(ent->modeldata.namex, ent->modeldata.namey, 0, 0, "%s", ent->name);
16405     }
16406 
16407     if(ent->modeldata.icon.position.x > -1000 &&  ent->modeldata.icon.position.y > -1000)
16408     {
16409         if(ent->health <= 0)
16410         {
16411             icon = ent->modeldata.icon.die;
16412         }
16413         else if(ent->inpain)
16414         {
16415             icon = ent->modeldata.icon.pain;
16416         }
16417         else if(ent->getting)
16418         {
16419             icon = ent->modeldata.icon.get;
16420         }
16421         else
16422         {
16423             icon = ent->modeldata.icon.def;
16424         }
16425 
16426         if(icon >= 0)
16427         {
16428             drawmethod = plainmethod;
16429             drawmethod.table = ent->modeldata.icon.usemap ? ent->colourmap : NULL;
16430             spriteq_add_sprite(ent->modeldata.icon.position.x, ent->modeldata.icon.position.y, HUD_Z, icon, &drawmethod, 0);
16431         }
16432     }
16433 
16434     if(ent->modeldata.health && ent->modeldata.hpx > -1000 && ent->modeldata.hpy > -1000)
16435     {
16436         bar(ent->modeldata.hpx, ent->modeldata.hpy, ent->oldhealth, ent->modeldata.health, &(ent->modeldata.hpbarstatus));
16437     }
16438 }
16439 
16440 
drawstatus()16441 void drawstatus()
16442 {
16443     int i;
16444 
16445     for(i = 0; i < MAX_PLAYERS; i++)
16446     {
16447         if(player[i].ent)
16448         {
16449             // Health bars
16450             bar(videomodes.shiftpos[i] + plife[i][0], savedata.windowpos + plife[i][1], player[i].ent->oldhealth, player[i].ent->modeldata.health, &lbarstatus);
16451             if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife && player[i].ent->opponent->modeldata.health)
16452             {
16453                 bar(videomodes.shiftpos[i] + elife[i][0], savedata.windowpos + elife[i][1], player[i].ent->opponent->oldhealth, player[i].ent->opponent->modeldata.health, &olbarstatus);    // Tied in with the nolife flag
16454             }
16455             // Draw mpbar
16456             if(player[i].ent->modeldata.mp)
16457             {
16458                 bar(videomodes.shiftpos[i] + pmp[i][0], savedata.windowpos + pmp[i][1], player[i].ent->oldmp, player[i].ent->modeldata.mp, &mpbarstatus);
16459             }
16460         }
16461     }
16462 
16463     // Time box
16464     if(!level->notime && !timeloc[4])    // Only draw if notime is set to 0 or not specified
16465     {
16466         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);
16467         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);
16468         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);
16469         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);
16470         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);
16471         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);
16472         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);
16473         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);
16474     }
16475 }
16476 
update_loading(s_loadingbar * s,int value,int max)16477 void update_loading(s_loadingbar *s,  int value, int max)
16478 {
16479     static unsigned int lasttick = 0;
16480     static unsigned int soundtick = 0;
16481     static unsigned int keybtick = 0;
16482     int pos_x = s->bar_position.x + videomodes.hShift;
16483     int pos_y = s->bar_position.y + videomodes.vShift;
16484     int size_x = s->bsize;
16485     int text_x = s->text_position.x + videomodes.hShift;
16486     int text_y = s->text_position.y + videomodes.vShift;
16487     unsigned int ticks = timer_gettick();
16488 #ifdef PSP
16489     ticks /= 1000; //temp solution
16490 #endif
16491     if(ticks - soundtick > 20)
16492     {
16493         sound_update_music();
16494         soundtick = ticks;
16495     }
16496 
16497     if(ticks - keybtick > 250)
16498     {
16499         control_update(playercontrolpointers, 1); // Respond to exit and/or fullscreen requests from user/OS
16500         keybtick = ticks;
16501     }
16502 
16503 
16504     if(ticks - lasttick > s->refreshMs || value < 0 || value == max)   // Negative value forces a repaint. used when only bg is drawn for the first time
16505     {
16506         spriteq_clear();
16507         execute_loading_script(value, max);
16508         if(s->set)
16509         {
16510             if (value < 0)
16511             {
16512                 value = 0;
16513             }
16514             if(isLoadingScreenTypeBar(s->set))
16515             {
16516                 loadingbarstatus.size.x = size_x;
16517                 bar(pos_x, pos_y, value, max, &loadingbarstatus);
16518             }
16519             font_printf(text_x, text_y, s->tf, 0, Tr("Loading..."));
16520             if(isLoadingScreenTypeBg(s->set))
16521             {
16522                 if(background)
16523                 {
16524                     putscreen(vscreen, background, 0, 0, NULL);
16525                 }
16526                 else
16527                 {
16528                     clearscreen(vscreen);
16529                 }
16530             }
16531             spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0);
16532             video_copy_screen(vscreen);
16533             spriteq_clear();
16534         }
16535         else if(value < 0)   // Original BOR v1.0029 used this method.  Since loadingbg is optional, we should print this one again.
16536         {
16537             clearscreen(vscreen);
16538             font_printf(120 + videomodes.hShift, 110 + videomodes.vShift, 0, 0, Tr("Loading..."));
16539             spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0);
16540             video_copy_screen(vscreen);
16541             spriteq_clear();
16542         }
16543         lasttick = ticks;
16544     }
16545 }
16546 
addscore(int playerindex,int add)16547 void addscore(int playerindex, int add)
16548 {
16549     unsigned int s ;
16550     unsigned int next1up;
16551     ScriptVariant var; // used for execute script
16552     Script *cs;
16553 
16554     if(playerindex < 0)
16555     {
16556         return;    //dont score if <0, e.g., npc damage enemy, enemy damage enemy
16557     }
16558 
16559     playerindex &= 3;
16560 
16561     s = player[playerindex].score;
16562     cs = score_script + playerindex;
16563 
16564     next1up = ((s / lifescore) + 1) * lifescore;
16565 
16566     s += add;
16567     if(s > 999999999)
16568     {
16569         s = 999999999;
16570     }
16571 
16572     while(s > next1up)
16573     {
16574 
16575         if(SAMPLE_1UP >= 0)
16576         {
16577             sound_play_sample(SAMPLE_1UP, 0, savedata.effectvol, savedata.effectvol, 100);
16578         }
16579 
16580         player[playerindex].lives++;
16581         next1up += lifescore;
16582     }
16583 
16584     player[playerindex].score = s;
16585 
16586     //execute a script then
16587     if(Script_IsInitialized(cs))
16588     {
16589         ScriptVariant_Init(&var);
16590         ScriptVariant_ChangeType(&var, VT_INTEGER);
16591         var.lVal = (LONG)add;
16592         Script_Set_Local_Variant(cs, "score", &var);
16593         Script_Execute(cs);
16594         ScriptVariant_Clear(&var);
16595         Script_Set_Local_Variant(cs, "score", &var);
16596     }
16597 }
16598 
16599 
16600 
16601 
16602 // ---------------------------- Object handling ------------------------------
16603 
free_ent(entity * e)16604 void free_ent(entity *e)
16605 {
16606     if(!e)
16607     {
16608         return;
16609     }
16610     clear_all_scripts(e->scripts, 2);
16611     free_all_scripts(&e->scripts);
16612 
16613     if(e->waypoints)
16614     {
16615         free(e->waypoints);
16616         e->waypoints = NULL;
16617     }
16618     if(e->defense)
16619     {
16620         free(e->defense);
16621         e->defense = NULL;
16622     }
16623     if(e->offense_factors)
16624     {
16625         free(e->offense_factors);
16626         e->offense_factors = NULL;
16627     }
16628     if(e->varlist)
16629     {
16630         // Although free_ent will be only called once when the engine is shutting down,
16631         // just clear those in case we forget something
16632         Varlist_Clear(e->varlist);
16633         free(e->varlist);
16634         e->varlist = NULL;
16635     }
16636     free(e);
16637     e = NULL;
16638 }
16639 
free_ents()16640 void free_ents()
16641 {
16642     int i;
16643     if(!ent_list)
16644     {
16645         return;
16646     }
16647     for(i = 0; i < ent_list_size; i++)
16648     {
16649         free_ent(ent_list[i]);
16650     }
16651     free(ent_list);
16652     free(ent_stack);
16653     ent_list = ent_stack = NULL;
16654     ent_list_size = ent_max = ent_count = ent_stack_size = 0;
16655 }
16656 
alloc_ent()16657 entity *alloc_ent()
16658 {
16659     entity *ent = malloc(sizeof(*ent));
16660     memset(ent, 0, sizeof(*ent));
16661     ent->defense = malloc(sizeof(*ent->defense) * max_attack_types);
16662     memset(ent->defense, 0, sizeof(*ent->defense)*max_attack_types);
16663     ent->offense_factors = malloc(sizeof(*ent->offense_factors) * max_attack_types);
16664     memset(ent->offense_factors, 0, sizeof(*ent->offense_factors)*max_attack_types);
16665     ent->varlist = calloc(1, sizeof(*ent->varlist));
16666     // memset should be OK by know, because VT_EMPTY is zero by value, or else we should use ScriptVariant_Init
16667     Varlist_Init(ent->varlist, max_entity_vars);
16668     alloc_all_scripts(&ent->scripts);
16669     return ent;
16670 }
16671 
16672 
alloc_ents()16673 int alloc_ents()
16674 {
16675     int i;
16676 
16677     if(ent_list_size >= maxentities)
16678     {
16679         return 0;
16680     }
16681 
16682     ent_list_size += MAX_ENTS;
16683 
16684     ent_list = realloc(ent_list, sizeof(*ent_list) * ent_list_size);
16685     ent_stack = realloc(ent_stack, sizeof(*ent_list) * ent_list_size);
16686 
16687     for(i = ent_list_size - MAX_ENTS; i < ent_list_size; i++)
16688     {
16689         ent_list[i] = alloc_ent();
16690         ent_list[i]->sortid = i * 100;
16691         ent_stack[i] = NULL;
16692     }
16693     //ent_count = ent_max = 0;
16694     return 1;
16695 }
16696 
16697 
16698 //UT: merged DC's common walk/idle functions
common_anim_series(entity * ent,int arraya[],int maxa,int forcemode,int defaulta)16699 static int common_anim_series(entity *ent, int arraya[], int maxa, int forcemode, int defaulta)
16700 {
16701     int i, b, e;                                                                    //Loop counter.
16702     int iAni;                                                                       //Animation.
16703 
16704     b = forcemode ? forcemode - 1 : 0;
16705     e = forcemode ? forcemode : maxa;
16706 
16707     for (i = b; i < e; i++)															//Loop through all relevant animations.
16708     {
16709         iAni = arraya[i];															//Get current animation.
16710 
16711         if (validanim(ent, iAni) && iAni != defaulta)                               //Valid and not Default animation??
16712         {
16713             if (forcemode || normal_find_target(iAni, 0))                           //Opponent in range of current animation?
16714             {
16715                 ent_set_anim(ent, iAni, 0);                                         //Set animation.
16716                 if ( defaulta == ANI_WALK || defaulta == ANI_UP || defaulta == ANI_DOWN ) ent->walking = 1; // set walking prop
16717 
16718                 return 1;                                                           //Return 1 and exit.
16719             }
16720         }
16721     }
16722 
16723     if (validanim(ent, defaulta))
16724     {
16725         ent_set_anim(ent, defaulta, 0);                                             //No alternates were set. Set default..
16726         if ( defaulta == ANI_WALK || defaulta == ANI_UP || defaulta == ANI_DOWN ) ent->walking = 1; // set walking prop
16727 
16728         return 1;                                                                   //Return 1 and exit.
16729     }
16730 
16731     return 0;
16732 }
16733 
common_idle_anim(entity * ent)16734 int common_idle_anim(entity *ent)
16735 {
16736     /*
16737     common_idle_anim
16738     Damon Vaughn Caskey
16739     11012009
16740     Determine and set appropriate idle animation based on condition and range.
16741     Returns 1 if any animation is set.
16742     */
16743     entity *tempself = self;
16744 
16745     self = ent;
16746 
16747     if(self->idling)
16748     {
16749         self->idling |= 2;
16750     }
16751 
16752     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
16753     {
16754         ent->velocity.x = ent->velocity.z = 0;    //Stop movement.
16755     }
16756 
16757     if(validanim(ent, ANI_FAINT) && ent->health <= ent->modeldata.health / 4)           //ANI_FAINT and health at/below 25%?
16758     {
16759         ent_set_anim(ent, ANI_FAINT, 0);                                                //Set ANI_FAINT.
16760         goto found;                                                                      //Return 1 and exit.
16761     }
16762     else if(validanim(ent, ANI_SLEEP) && time > ent->sleeptime)    //ANI_SLEEP, sleeptime up
16763     {
16764         ent_set_anim(ent, ANI_SLEEP, 0);                                                //Set sleep anim.
16765         goto found;                                                                     //Return 1 and exit.
16766     }
16767     else if(common_anim_series(ent, animidles, max_idles, ent->idlemode, ANI_IDLE))
16768     {
16769         goto found;
16770     }
16771 
16772     self = tempself;
16773     return 0;
16774 found:
16775     self = tempself;
16776     return 1;
16777 }
16778 
16779 
16780 #define common_walk_anim(ent) \
16781 	common_anim_series(ent, animwalks, max_walks, ent->walkmode, ANI_WALK)
16782 
16783 #define common_backwalk_anim(ent) \
16784 	common_anim_series(ent, animbackwalks, max_backwalks, ent->walkmode, ANI_BACKWALK)
16785 
16786 #define common_up_anim(ent) \
16787 	common_anim_series(ent, animups, max_ups, ent->walkmode, ANI_UP)
16788 
16789 #define common_down_anim(ent) \
16790 	common_anim_series(ent, animdowns, max_downs, ent->walkmode, ANI_DOWN)
16791 
16792 
16793 // This function find nearst x escape point from the given position
16794 // It assumes the point is already inside the wall
find_nearest_wall_x(int wall,float x,float z)16795 static float find_nearest_wall_x(int wall, float x, float z)
16796 {
16797     float x1, x2;
16798 
16799     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);
16800     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);
16801 
16802     if(diff(x1, x) > diff(x2, x))
16803     {
16804         return x2;
16805     }
16806     else
16807     {
16808         return x1;
16809     }
16810 }
16811 
16812 // this method initialize an entity's A.I. behaviors
ent_default_init(entity * e)16813 void ent_default_init(entity *e)
16814 {
16815     int dodrop;
16816     int wall;
16817     entity *other;
16818 
16819     if(!e)
16820     {
16821         return;
16822     }
16823 
16824     if((!selectScreen && !time) || e->modeldata.type != TYPE_PLAYER )
16825     {
16826         if( validanim(e, ANI_SPAWN))
16827         {
16828             ent_set_anim(e, ANI_SPAWN, 0);    // use new playerselect spawn anim
16829         }
16830         else if( validanim(e, ANI_RESPAWN))
16831         {
16832             ent_set_anim(e, ANI_RESPAWN, 0);
16833         }
16834         //else set_idle(e);
16835     }
16836     else if(!selectScreen && time && e->modeldata.type == TYPE_PLAYER) // mid-level respawn
16837     {
16838         if( validanim(e, ANI_RESPAWN))
16839         {
16840             ent_set_anim(e, ANI_RESPAWN, 0);
16841         }
16842         else if( validanim(e, ANI_SPAWN))
16843         {
16844             ent_set_anim(e, ANI_SPAWN, 0);
16845         }
16846         //else set_idle(e);
16847     }
16848     else if(selectScreen && validanim(e, ANI_SELECT))
16849     {
16850         ent_set_anim(e, ANI_SELECT, 0);
16851     }
16852     //else set_idle(e);
16853 
16854     if(!level)
16855     {
16856         if(!e->animation)
16857         {
16858             set_idle(e);
16859         }
16860         return;
16861     }
16862 
16863     e->nograb_default = 0; // init all entities to 0 by default
16864 
16865     switch(e->modeldata.type)
16866     {
16867     case TYPE_RESERVED:
16868         //Do nothing.
16869         break;
16870     case TYPE_ENDLEVEL:
16871     case TYPE_ITEM:
16872         e->nograb = 1;
16873         e->nograb_default = e->nograb;
16874         break;
16875 
16876     case TYPE_PLAYER:
16877         //e->direction = (level->scrolldir != SCROLL_LEFT);
16878         e->takedamage = player_takedamage;
16879         e->think = player_think;
16880         e->trymove = player_trymove;
16881 
16882         if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN))
16883         {
16884             e->takeaction = common_spawn;
16885         }
16886         else if(!e->animation)
16887         {
16888             if(time && level->spawn[(int)e->playerindex].y > e->position.y)
16889             {
16890                 e->takeaction = common_drop;
16891                 e->position.y = (float)level->spawn[(int)e->playerindex].y;
16892                 if(validanim(e, ANI_JUMP))
16893                 {
16894                     ent_set_anim(e, ANI_JUMP, 0);
16895                 }
16896             }
16897         }
16898         if(time && e->modeldata.makeinv)
16899         {
16900             // Spawn invincible code
16901             e->invincible = 1;
16902             e->blink = (e->modeldata.makeinv > 0);
16903             e->invinctime = time + ABS(e->modeldata.makeinv);
16904             e->arrowon = 1;    // Display the image above the player
16905         }
16906         break;
16907     case TYPE_NPC: // use NPC(or A.I. player) instread of an enemy subtype or trap subtype, for further A.I. use
16908         if(e->modeldata.multiple == 0)
16909         {
16910             e->modeldata.multiple = -1;
16911         }
16912 
16913     case TYPE_ENEMY:
16914         e->think = common_think;
16915         if(e->modeldata.subtype == SUBTYPE_BIKER)
16916         {
16917             e->nograb = 1;
16918             e->nograb_default = e->nograb;
16919             e->attacking = 1;
16920             //e->direction = (e->position.x<0);
16921             if(e->modeldata.speed)
16922             {
16923                 e->velocity.x = (e->direction == DIRECTION_RIGHT) ? (e->modeldata.speed) : (-e->modeldata.speed);
16924             }
16925             else
16926             {
16927                 e->velocity.x = (e->direction == DIRECTION_RIGHT) ? (1.7 + randf((float)0.6)) : (-(1.7 + randf((float)0.6)));
16928             }
16929             e->takedamage = biker_takedamage;
16930             e->speedmul = 2;
16931             break;
16932         }
16933         // define new subtypes
16934         else if(e->modeldata.subtype == SUBTYPE_ARROW || e->modeldata.subtype == SUBTYPE_BOOMERANG)
16935         {
16936             e->health = 1;
16937             if(!e->modeldata.speed && !e->modeldata.nomove)
16938             {
16939                 e->modeldata.speed = 2;    // Set default speed to 2 for arrows
16940             }
16941             else if(e->modeldata.nomove)
16942             {
16943                 e->modeldata.speed = 0;
16944             }
16945             if(e->ptype)
16946             {
16947                 e->base = 0;
16948             }
16949             else
16950             {
16951                 e->base = e->position.y;
16952             }
16953             e->nograb = 1;
16954             e->nograb_default = e->nograb;
16955             e->attacking = 1;
16956             e->takedamage = arrow_takedamage;
16957             e->speedmul = 2;
16958             break;
16959         }
16960         else
16961         {
16962             e->trymove = common_trymove;
16963             // Must just be a regular enemy, set defaults accordingly
16964             if(!e->modeldata.speed && !e->modeldata.nomove)
16965             {
16966                 e->modeldata.speed = 1;
16967             }
16968             else if(e->modeldata.nomove)
16969             {
16970                 e->modeldata.speed = 0;
16971             }
16972             if(e->modeldata.multiple == 0)
16973             {
16974                 e->modeldata.multiple = 5;
16975             }
16976             e->takedamage = common_takedamage;//enemy_takedamage;
16977         }
16978 
16979         if(e->modeldata.subtype == SUBTYPE_NOTGRAB)
16980         {
16981             e->nograb = 1;
16982             e->nograb_default = e->nograb;
16983         }
16984 
16985         if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN))
16986         {
16987             e->takeaction = common_spawn;
16988         }
16989         else
16990         {
16991             dodrop = (e->modeldata.subtype != SUBTYPE_ARROW && e->modeldata.subtype != SUBTYPE_BOOMERANG && level && (level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN));
16992 
16993             if(!nodropspawn && (dodrop || (e->position.x > advancex - 30 && e->position.x < advancex + videomodes.hRes + 30 && e->position.y == 0)) )
16994             {
16995                 e->position.y += videomodes.vRes + randf(40);
16996             }
16997             if(inair(e))
16998             {
16999                 e->takeaction = common_drop;//enemy_drop;
17000                 if(validanim(e, ANI_JUMP))
17001                 {
17002                     ent_set_anim(e, ANI_JUMP, 0);
17003                 }
17004             }
17005         }
17006         break;
17007         // define trap type
17008     case TYPE_TRAP:
17009         e->think = trap_think;
17010         e->takedamage =  common_takedamage;//enemy_takedamage;
17011         e->speedmul = 2;
17012         break;
17013     case TYPE_OBSTACLE:
17014         e->nograb = 1;
17015         e->nograb_default = e->nograb;
17016         if(e->health <= 0)
17017         {
17018             e->dead = 1;    // so it won't get hit
17019         }
17020         e->takedamage = obstacle_takedamage;//obstacle_takedamage;
17021         break;
17022     case TYPE_STEAMER:
17023         e->nograb = 1;
17024         e->nograb_default = e->nograb;
17025         e->think = steamer_think;
17026         e->base = e->position.y;
17027         break;
17028     case TYPE_TEXTBOX:    // New type for displaying text purposes
17029         e->nograb = 1;
17030         e->nograb_default = e->nograb;
17031         e->think = text_think;
17032         break;
17033     case TYPE_SHOT:
17034         e->health = 1;
17035         e->nograb = 1;
17036         e->nograb_default = e->nograb;
17037         e->think = common_think;
17038         e->takedamage = arrow_takedamage;
17039         e->attacking = 1;
17040         if(!e->model->speed && !e->modeldata.nomove)
17041         {
17042             e->modeldata.speed = 2;    // Set default speed to 2 for arrows
17043         }
17044         else if(e->modeldata.nomove)
17045         {
17046             e->modeldata.speed = 0;
17047         }
17048         if(e->ptype)
17049         {
17050             e->base = 0;
17051         }
17052         else
17053         {
17054             e->base = e->position.y;
17055         }
17056         e->speedmul = 2;
17057         break;
17058     case TYPE_NONE:
17059         e->nograb = 1;
17060         e->nograb_default = e->nograb;
17061         if(e->modeldata.subject_to_gravity < 0)
17062         {
17063             e->modeldata.subject_to_gravity = 1;
17064         }
17065         //e->base=e->position.y; //complained?
17066         if(e->modeldata.no_adjust_base < 0)
17067         {
17068             e->modeldata.no_adjust_base = 1;
17069         }
17070 
17071         if(validanim(e, ANI_WALK))
17072         {
17073             if(e->direction == DIRECTION_RIGHT)
17074             {
17075                 e->velocity.x = e->modeldata.speed;
17076             }
17077             else
17078             {
17079                 e->velocity.x = -(e->modeldata.speed);
17080             }
17081             e->think = anything_walk;
17082 
17083             common_walk_anim(e);
17084             //ent_set_anim(e, ANI_WALK, 0);
17085         }
17086         else
17087         {
17088             e->speedmul = 2;
17089         }
17090         e->trymove = common_trymove;
17091         break;
17092     case TYPE_PANEL:
17093         e->nograb = 1;
17094         e->nograb_default = e->nograb;
17095         break;
17096     }
17097 
17098     if(!e->animation)
17099     {
17100         set_idle(e);
17101     }
17102 
17103     if(e->modeldata.multiple < 0)
17104     {
17105         e->modeldata.multiple = 0;
17106     }
17107 
17108     if(e->modeldata.subject_to_platform > 0 && (other = check_platform_below(e->position.x, e->position.z, e->position.y, e)))
17109     {
17110         e->base = other->position.y + other->animation->platform[other->animpos][7];
17111     }
17112     else if(e->modeldata.subject_to_wall > 0 && (wall = checkwall_below(e->position.x, e->position.z, 9999999)) >= 0)
17113     {
17114         if(level->walls[wall].height > MAX_WALL_HEIGHT)
17115         {
17116             e->position.x = find_nearest_wall_x(wall, e->position.x, e->position.z);
17117         }
17118         else
17119         {
17120             e->base = level->walls[wall].height;
17121         }
17122     }
17123 }
17124 
ent_spawn_ent(entity * ent)17125 void ent_spawn_ent(entity *ent)
17126 {
17127     entity *s_ent = NULL;
17128     float *spawnframe = ent->animation->spawnframe;
17129     float dy = level ? 4.0 : 0.0;
17130     // spawn point relative to current entity
17131     if(spawnframe[4] == 0)
17132     {
17133         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);
17134     }
17135     //relative to screen position
17136     else if(spawnframe[4] == 1)
17137     {
17138         if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN))
17139         {
17140             s_ent = spawn(advancex + spawnframe[1], advancey + spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17141         }
17142         else
17143         {
17144             s_ent = spawn(advancex + spawnframe[1], spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17145         }
17146     }
17147     //absolute position in level
17148     else
17149     {
17150         s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3] + 0.001, 0, NULL, ent->animation->subentity, NULL);
17151     }
17152 
17153     if(s_ent)
17154     {
17155         //ent_default_init(s_ent);
17156         if(s_ent->modeldata.type & TYPE_SHOT)
17157         {
17158             s_ent->playerindex = ent->playerindex;
17159         }
17160         if(s_ent->modeldata.subtype == SUBTYPE_ARROW || s_ent->modeldata.subtype == SUBTYPE_BOOMERANG)
17161         {
17162             s_ent->owner = ent;
17163         }
17164         s_ent->parent = ent;  //maybe used by A.I.
17165         execute_onspawn_script(s_ent);
17166     }
17167 }
17168 
ent_summon_ent(entity * ent)17169 void ent_summon_ent(entity *ent)
17170 {
17171     entity *s_ent = NULL;
17172     float *spawnframe = ent->animation->summonframe;
17173     float dy = level ? 4.0 : 0.0;
17174     // spawn point relative to current entity
17175     if(spawnframe[4] == 0)
17176     {
17177         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);
17178     }
17179     //relative to screen position
17180     else if(spawnframe[4] == 1)
17181     {
17182         if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN))
17183         {
17184             s_ent = spawn(advancex + spawnframe[1], advancey + spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17185         }
17186         else
17187         {
17188             s_ent = spawn(advancex + spawnframe[1], spawnframe[2] + dy, spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17189         }
17190     }
17191     //absolute position in level
17192     else
17193     {
17194         s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
17195     }
17196 
17197     if(s_ent)
17198     {
17199         if(!spawnframe[4])
17200         {
17201             s_ent->direction = ent->direction;
17202         }
17203         //ent_default_init(s_ent);
17204         if(s_ent->modeldata.type & TYPE_SHOT)
17205         {
17206             s_ent->playerindex = ent->playerindex;
17207         }
17208         if(s_ent->modeldata.subtype == SUBTYPE_ARROW || s_ent->modeldata.subtype == SUBTYPE_BOOMERANG)
17209         {
17210             s_ent->owner = ent;
17211         }
17212         //maybe used by A.I.
17213         s_ent->parent = ent;
17214         ent->subentity = s_ent;
17215         execute_onspawn_script(s_ent);
17216     }
17217 }
17218 
calculate_edelay(entity * ent,int f)17219 int calculate_edelay(entity *ent, int f)
17220 {
17221     int iDelay, iED_Mode, iED_Capmin, iED_CapMax, iED_RangeMin, iED_RangeMax;
17222     float fED_Factor;
17223     s_anim *anim = ent->animation;
17224 
17225     iDelay          = anim->delay[f];
17226     iED_Mode        = ent->modeldata.edelay.mode;
17227     fED_Factor      = ent->modeldata.edelay.factor;
17228     iED_Capmin      = ent->modeldata.edelay.cap.min;
17229     iED_CapMax      = ent->modeldata.edelay.cap.max;
17230     iED_RangeMin    = ent->modeldata.edelay.range.min;
17231     iED_RangeMax    = ent->modeldata.edelay.range.max;
17232 
17233     if (iDelay >= iED_RangeMin && iDelay <= iED_RangeMax) //Regular delay within ignore ranges?
17234     {
17235         switch(iED_Mode)
17236         {
17237         case EDELAY_MODE_MULTIPLY:
17238             iDelay = (int)(iDelay * fED_Factor);
17239             break;
17240         case EDELAY_MODE_ADD:
17241         default:
17242             iDelay += (int)fED_Factor;
17243             break;
17244         }
17245 
17246         if (iED_Capmin && iDelay < iED_Capmin)
17247         {
17248             iDelay = iED_Capmin;
17249         }
17250         if (iED_CapMax && iDelay > iED_CapMax)
17251         {
17252             iDelay = iED_CapMax;
17253         }
17254     }
17255     return iDelay;
17256 }
17257 
17258 // move here to prevent some duplicated code in ent_sent_anim and update_ents
update_frame(entity * ent,int f)17259 void update_frame(entity *ent, int f)
17260 {
17261     entity *tempself;
17262     entity *dust;
17263     s_collision_attack attack;
17264     s_axis_f move;
17265     s_anim *anim = ent->animation;
17266 
17267     if(f >= anim->numframes) // prevent a crash with invalid frame index.
17268     {
17269         return;
17270     }
17271 
17272     //important!
17273     tempself = self;
17274     self = ent;
17275 
17276     self->animpos = f;
17277     //self->currentsprite = self->animation->sprite[f];
17278 
17279     if(self->animating)
17280     {
17281         self->nextanim = time + calculate_edelay(self, f);
17282         self->pausetime = 0;
17283         execute_animation_script(self);
17284     }
17285 
17286     if(ent->animation != anim || ent->animpos != f)
17287     {
17288         goto uf_interrupted;
17289     }
17290 
17291     if(level && (anim->move[f]->x || anim->move[f]->z))
17292     {
17293         move.x = (float)(anim->move[f]->x ? anim->move[f]->x : 0);
17294         move.z = (float)(anim->move[f]->z ? anim->move[f]->z : 0);
17295         if(self->direction == DIRECTION_LEFT)
17296         {
17297             move.x = -move.x;
17298         }
17299         self->movex += move.x;
17300         self->movez += move.z;
17301     }
17302 
17303     if(anim->move[0]->base && anim->move[0]->base >= 0 && self->base <= 0)
17304     {
17305         ent->base = (float)anim->move[0]->base;
17306     }
17307     else if(!anim->move[0]->base || anim->move[0]->base < 0)
17308     {
17309         move.y = (float)(anim->move[f]->y ? anim->move[f]->y : 0);
17310         self->base += move.y;
17311         if(move.y != 0)
17312         {
17313             self->altbase += move.y;
17314         }
17315         else
17316         {
17317             self->altbase = 0;
17318         }
17319     }
17320 
17321     if(anim->flipframe == f)
17322     {
17323         self->direction = !self->direction;
17324     }
17325 
17326     if(anim->weaponframe && anim->weaponframe[0] == f)
17327     {
17328         dropweapon(2);
17329         set_weapon(self, anim->weaponframe[1], 0);
17330         if(!anim->weaponframe[2])
17331         {
17332             set_idle(self);
17333         }
17334     }
17335 
17336     if(anim->quakeframe.framestart + anim->quakeframe.cnt == f)
17337     {
17338         if(level)
17339         {
17340             if(anim->quakeframe.cnt % 2 || anim->quakeframe.v > 0)
17341             {
17342                 level->quake = anim->quakeframe.v;
17343             }
17344             else
17345             {
17346                 level->quake = anim->quakeframe.v * -1;
17347             }
17348         }
17349         if((anim->quakeframe.repeat - anim->quakeframe.cnt) > 1)
17350         {
17351             anim->quakeframe.cnt++;
17352         }
17353         else
17354         {
17355             anim->quakeframe.cnt = 0;
17356         }
17357     }
17358 
17359     if(anim->unsummonframe == f)
17360     {
17361         if(self->subentity)
17362         {
17363             self = self->subentity;
17364             attack = emptyattack;
17365             attack.dropv.y = default_model_dropv.y;
17366             attack.dropv.x = default_model_dropv.x;
17367             attack.dropv.z = default_model_dropv.z;
17368             attack.attack_force = self->health;
17369             attack.attack_type = max_attack_types;
17370             if(self->takedamage)
17371             {
17372                 self->takedamage(self, &attack);
17373             }
17374             else
17375             {
17376                 kill(self);
17377             }
17378             self = ent; // lol ...
17379             self->subentity = NULL;
17380         }
17381     }
17382 
17383     //spawn / summon /unsummon features
17384     if(anim->spawnframe && anim->spawnframe[0] == f && anim->subentity >= 0)
17385     {
17386         ent_spawn_ent(self);
17387     }
17388 
17389     if(anim->summonframe && anim->summonframe[0] == f && anim->subentity >= 0)
17390     {
17391         //subentity is dead
17392         if(!self->subentity || self->subentity->dead)
17393         {
17394             ent_summon_ent(self);
17395         }
17396     }
17397 
17398     if(anim->soundtoplay && anim->soundtoplay[f] >= 0)
17399     {
17400         sound_play_sample(anim->soundtoplay[f], 0, savedata.effectvol, savedata.effectvol, 100);
17401     }
17402 
17403     if(anim->jumpframe.frame == f)
17404     {
17405         // Set custom jumpheight for jumpframes
17406         /*if(self->animation->jumpframe.v > 0)*/ toss(self, anim->jumpframe.velocity.y);
17407         self->velocity.x = self->direction == DIRECTION_RIGHT ? anim->jumpframe.velocity.x : -anim->jumpframe.velocity.x;
17408         self->velocity.z = anim->jumpframe.velocity.z;
17409 
17410         if(anim->jumpframe.ent >= 0)
17411         {
17412             dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, anim->jumpframe.ent, NULL);
17413             if(dust)
17414             {
17415                 dust->base = self->position.y;
17416                 dust->autokill = 2;
17417                 execute_onspawn_script(dust);
17418             }
17419         }
17420     }
17421 
17422     if(anim->projectile.throwframe == f)
17423     {
17424         // For backward compatible thing
17425         // throw stars in the air, hmm, strange
17426         // custstar custknife in animation should be checked first
17427         // then if the entity is jumping, check star first, if failed, try knife instead
17428         // well, try knife at last, if still failed, try star, or just let if shutdown?
17429 #define __trystar star_spawn(self->position.x + (self->direction == DIRECTION_RIGHT ? 56 : -56), self->position.z, self->position.y+67, self->direction)
17430 #define __tryknife knife_spawn(NULL, -1, self->position.x, self->position.z, self->position.y + anim->projectile.position.y, self->direction, 0, 0)
17431         if(anim->projectile.knife >= 0 || anim->projectile.flash >= 0)
17432         {
17433             __tryknife;
17434         }
17435         else if(anim->projectile.star >= 0)
17436         {
17437             __trystar;
17438         }
17439         else if(self->jumping)
17440         {
17441             if(!__trystar)
17442             {
17443                 __tryknife;
17444             }
17445         }
17446         else if(!__tryknife)
17447         {
17448             __trystar;
17449         }
17450         self->reactive = 1;
17451     }
17452 
17453     if(anim->projectile.shootframe == f)
17454     {
17455         knife_spawn(NULL, -1, self->position.x, self->position.z, self->position.y, self->direction, 1, 0);
17456         self->reactive = 1;
17457     }
17458 
17459     if(anim->projectile.tossframe == f)
17460     {
17461         bomb_spawn(NULL, -1, self->position.x, self->position.z, self->position.y + anim->projectile.position.y, self->direction, 0);
17462         self->reactive = 1;
17463     }
17464 
17465 uf_interrupted:
17466 
17467     //important!
17468     self = tempself;
17469 }
17470 
17471 
ent_set_anim(entity * ent,int aninum,int resetable)17472 void ent_set_anim(entity *ent, int aninum, int resetable)
17473 {
17474     s_anim *ani = NULL;
17475     int animpos;
17476 
17477     if(!ent)
17478     {
17479         //printf("FATAL: tried to set animation with invalid address (no such object)");
17480         return;
17481     }
17482 
17483     if(aninum < 0 || aninum >= max_animations)
17484     {
17485         //printf("FATAL: tried to set animation with invalid index (%s, %i)", ent->name, aninum);
17486         return;
17487     }
17488 
17489     if(!validanim(ent, aninum))
17490     {
17491         //printf("FATAL: tried to set animation with invalid address (%s, %i)", ent->name, aninum);
17492         return;
17493     }
17494 
17495     ani = ent->modeldata.animation[aninum];
17496 
17497     if(!resetable && ent->animation == ani)
17498     {
17499         return;
17500     }
17501 
17502     if(ani->numframes == 0)
17503     {
17504         return;
17505     }
17506 
17507     if(ent->animation && ((resetable & 2) || (ani->sync >= 0 && ent->animation->sync == ani->sync)))
17508     {
17509         animpos = ent->animpos;
17510         if(animpos >= ani->numframes)
17511         {
17512             animpos = 0;
17513         }
17514         ent->animnum = aninum;
17515         ent->animation = ani;
17516         ent->animpos = animpos;
17517         ent->walking = 0;
17518     }
17519     else
17520     {
17521         ent->animation = ani;
17522         ent->animnum = aninum;    // Stored for nocost usage
17523         ent->animation->animhits = 0;
17524 
17525         ent->animating = 1;
17526         ent->lasthit = ent->grabbing;
17527         ent->altbase = 0;
17528         ent->walking = 0;
17529 
17530         update_frame(ent, 0);
17531     }
17532 }
17533 
model_get_colourmap(s_model * model,unsigned which)17534 unsigned char *model_get_colourmap(s_model *model, unsigned which)
17535 {
17536     if(which <= 0 || which > model->maps_loaded)
17537     {
17538         return model->palette;
17539     }
17540     else
17541     {
17542         return model->colourmap[which - 1];
17543     }
17544 }
17545 
17546 // 0 = none, 1+ = alternative
ent_set_colourmap(entity * ent,unsigned int which)17547 void ent_set_colourmap(entity *ent, unsigned int which)
17548 {
17549     if(which > ent->modeldata.maps_loaded)
17550     {
17551         which = 0;
17552     }
17553     if(which <= 0)
17554     {
17555         ent->colourmap = ent->modeldata.palette;
17556     }
17557     else
17558     {
17559         ent->colourmap = ent->modeldata.colourmap[which - 1];
17560     }
17561     ent->map = which;
17562 }
17563 
17564 // used by ent_set_model
ent_copy_uninit(entity * ent,s_model * oldmodel)17565 void ent_copy_uninit(entity *ent, s_model *oldmodel)
17566 {
17567     if(ent->modeldata.multiple < 0)
17568     {
17569         ent->modeldata.multiple             = oldmodel->multiple;
17570     }
17571     if(ent->modeldata.subject_to_basemap < 0)
17572     {
17573         ent->modeldata.subject_to_basemap   = oldmodel->subject_to_basemap;
17574     }
17575     if(ent->modeldata.subject_to_wall < 0)
17576     {
17577         ent->modeldata.subject_to_wall      = oldmodel->subject_to_wall;
17578     }
17579     if(ent->modeldata.subject_to_platform < 0)
17580     {
17581         ent->modeldata.subject_to_platform  = oldmodel->subject_to_platform;
17582     }
17583     if(ent->modeldata.subject_to_obstacle < 0)
17584     {
17585         ent->modeldata.subject_to_obstacle  = oldmodel->subject_to_obstacle;
17586     }
17587     if(ent->modeldata.subject_to_hole < 0)
17588     {
17589         ent->modeldata.subject_to_hole      = oldmodel->subject_to_hole;
17590     }
17591     if(ent->modeldata.subject_to_gravity < 0)
17592     {
17593         ent->modeldata.subject_to_gravity   = oldmodel->subject_to_gravity;
17594     }
17595     if(ent->modeldata.subject_to_screen < 0)
17596     {
17597         ent->modeldata.subject_to_screen    = oldmodel->subject_to_screen;
17598     }
17599     if(ent->modeldata.subject_to_minz < 0)
17600     {
17601         ent->modeldata.subject_to_minz      = oldmodel->subject_to_minz;
17602     }
17603     if(ent->modeldata.subject_to_maxz < 0)
17604     {
17605         ent->modeldata.subject_to_maxz      = oldmodel->subject_to_maxz;
17606     }
17607     if(ent->modeldata.no_adjust_base < 0)
17608     {
17609         ent->modeldata.no_adjust_base       = oldmodel->no_adjust_base;
17610     }
17611     if(ent->modeldata.aimove == -1)
17612     {
17613         ent->modeldata.aimove               = oldmodel->aimove;
17614     }
17615     if(ent->modeldata.aiattack == -1)
17616     {
17617         ent->modeldata.aiattack             = oldmodel->aiattack;
17618     }
17619     if(ent->modeldata.hostile < 0)
17620     {
17621         ent->modeldata.hostile              = oldmodel->hostile;
17622     }
17623     if(ent->modeldata.candamage < 0)
17624     {
17625         ent->modeldata.candamage            = oldmodel->candamage;
17626     }
17627     if(ent->modeldata.projectilehit < 0)
17628     {
17629         ent->modeldata.projectilehit        = oldmodel->projectilehit;
17630     }
17631     if(!ent->modeldata.health)
17632     {
17633         ent->modeldata.health               = oldmodel->health;
17634     }
17635     if(!ent->modeldata.mp)
17636     {
17637         ent->modeldata.mp                   = oldmodel->mp;
17638     }
17639     if(ent->modeldata.risetime.rise == -1)
17640     {
17641         ent->modeldata.risetime.rise          = oldmodel->risetime.rise;
17642     }
17643     /*
17644     if(!ent->modeldata.antigrab)
17645     	ent->modeldata.antigrab             = oldmodel->antigrab;
17646     if(!ent->modeldata.grabforce)
17647     	ent->modeldata.grabforce            = oldmodel->grabforce;
17648     if(!ent->modeldata.paingrab)
17649     	ent->modeldata.paingrab             = oldmodel->paingrab;*/
17650 
17651     if(ent->health > ent->modeldata.health)
17652     {
17653         ent->health = ent->modeldata.health;
17654     }
17655     if(ent->mp > ent->modeldata.mp)
17656     {
17657         ent->mp = ent->modeldata.mp;
17658     }
17659 }
17660 
17661 
17662 //if syncAnim is set, only change animation reference
ent_set_model(entity * ent,char * modelname,int syncAnim)17663 void ent_set_model(entity *ent, char *modelname, int syncAnim)
17664 {
17665     s_model *m = NULL;
17666     s_model oldmodel;
17667     if(ent == NULL)
17668     {
17669         shutdown(1, "FATAL: tried to change model of invalid object");
17670     }
17671     m = findmodel(modelname);
17672     if(m == NULL)
17673     {
17674         shutdown(1, "Model not found: '%s'", modelname);
17675     }
17676     oldmodel = ent->modeldata;
17677     ent->model = m;
17678     ent->modeldata = *m;
17679     ent_copy_uninit(ent, &oldmodel);
17680     ent_set_colourmap(ent, ent->map);
17681 
17682     if(syncAnim && m->animation[ent->animnum])
17683     {
17684         ent->animation = m->animation[ent->animnum];
17685         if(ent->animpos >= ent->animation->numframes)
17686         {
17687             ent->animpos = ent->animation->numframes - 1;
17688         }
17689         ent->nextanim = time + calculate_edelay(ent, ent->animpos);
17690         //update_frame(ent, ent->animpos);
17691     }
17692     else
17693     {
17694         ent->attacking = 0;
17695 
17696         if((!selectScreen && !time) || !(ent->modeldata.type & TYPE_PLAYER))
17697         {
17698             // use new playerselect spawn anim
17699             if( validanim(ent, ANI_SPAWN))
17700             {
17701                 ent_set_anim(ent, ANI_SPAWN, 0);
17702             }
17703             else
17704             {
17705                 if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0);
17706             }
17707         }
17708         else if(!selectScreen && time && (ent->modeldata.type & TYPE_PLAYER))
17709         {
17710             // mid-level respawn
17711             if( validanim(ent, ANI_RESPAWN))
17712             {
17713                 ent_set_anim(ent, ANI_RESPAWN, 0);
17714             }
17715             else if( validanim(ent, ANI_SPAWN))
17716             {
17717                 ent_set_anim(ent, ANI_SPAWN, 0);
17718             }
17719             else
17720             {
17721                 if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0);
17722             }
17723         }
17724         else if(selectScreen && validanim(ent, ANI_SELECT))
17725         {
17726             ent_set_anim(ent, ANI_SELECT, 0);
17727         }
17728         else
17729         {
17730             if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0);
17731         }
17732     }
17733 }
17734 
17735 
spawn(float x,float z,float a,int direction,char * name,int index,s_model * model)17736 entity *spawn(float x, float z, float a, int direction, char *name, int index, s_model *model)
17737 {
17738     entity *e = NULL;
17739     int i, id;
17740     s_defense *dfs;
17741     float *ofs;
17742     Varlist *vars;
17743     s_scripts *scripts;
17744 
17745     if(!model)
17746     {
17747         if(index >= 0)
17748         {
17749             model = model_cache[index].model;
17750         }
17751         else if(name)
17752         {
17753             model = findmodel(name);
17754         }
17755     }
17756 
17757     // Be a bit more tolerant...
17758     if(model == NULL)
17759     {
17760         /*
17761         if(index>=0)
17762         	printf("FATAL: attempt to spawn object with invalid model cache id (%d)!\n", index);
17763         else if(name)
17764         	printf("FATAL: attempt to spawn object with invalid model name (%s)!\n", name);*/
17765         return NULL;
17766     }
17767 
17768     if(ent_count >= ent_list_size && !alloc_ents())
17769     {
17770         return NULL;    //out of memory ?
17771     }
17772 
17773     for(i = 0; i < ent_list_size; i++)
17774     {
17775         if(!ent_list[i]->exists || (ent_count >= spawnoverride && ent_list[i]->modeldata.priority < 0 && ent_list[i]->modeldata.priority <= model->priority))
17776         {
17777             e = ent_list[i];
17778             if(e->exists)
17779             {
17780                 kill(e);
17781             }
17782             // save these values, or they will loss when memset called
17783             id      = e->sortid;
17784             dfs     = e->defense;
17785             ofs     = e->offense_factors;
17786             vars    = e->varlist;
17787             Varlist_Cleanup(vars);
17788             memcpy(dfs, model->defense, sizeof(*dfs)*max_attack_types);
17789             memcpy(ofs, model->offense_factors, sizeof(*ofs)*max_attack_types);
17790             // clear up
17791             clear_all_scripts(e->scripts, 1);
17792             if(e->waypoints)
17793             {
17794                 free(e->waypoints);
17795             }
17796 
17797             scripts = e->scripts;
17798             memset(e, 0, sizeof(*e));
17799             e->drawmethod = plainmethod;
17800             e->drawmethod.flag = 0;
17801 
17802             // add to list and count current entities
17803             e->exists = 1;
17804             ent_count++;
17805 
17806             e->modeldata = *model; // copy the entir model data here
17807             e->model = model;
17808             e->defaultmodel = model;
17809 
17810             e->scripts = scripts;
17811             // copy from model a fresh script
17812 
17813             copy_all_scripts(model->scripts, e->scripts, 1);
17814 
17815             if(ent_count > ent_max)
17816             {
17817                 ent_max = ent_count;
17818             }
17819             e->timestamp = time; // log time so update function will ignore it if it is new
17820 
17821             e->health = e->modeldata.health;
17822             e->mp = e->modeldata.mp;
17823             e->knockdowncount = e->modeldata.knockdowncount;
17824             e->position.x = x;
17825             e->position.z = z;
17826             e->position.y = a;
17827             e->direction = direction;
17828             e->nextthink = time + 1;
17829             e->nextmove = time + 1;
17830             e->speedmul = 1;
17831             ent_set_colourmap(e, 0);
17832             e->lifespancountdown = model->lifespan; // new life span countdown
17833             if((e->modeldata.type & TYPE_PLAYER) && ((level && level->nohit) || savedata.mode))
17834             {
17835                 e->modeldata.hostile &= ~TYPE_PLAYER;
17836                 e->modeldata.candamage &= ~TYPE_PLAYER;
17837             }
17838             if(e->modeldata.type & TYPE_PLAYER)
17839             {
17840                 e->playerindex = currentspawnplayer;
17841             }
17842 
17843             if(e->modeldata.type & TYPE_TEXTBOX)
17844             {
17845                 textbox = e;
17846             }
17847 
17848             strncpy(e->name, e->modeldata.name, MAX_NAME_LEN);
17849             // copy back the value
17850             e->sortid = id;
17851             e->defense = dfs;
17852             e->offense_factors = ofs;
17853             e->varlist = vars;
17854             ent_default_init(e);
17855             return e;
17856         }
17857     }
17858     return NULL;
17859 }
17860 
17861 
17862 
17863 // Break the link an entity has with another one
ent_unlink(entity * e)17864 void ent_unlink(entity *e)
17865 {
17866     if(e->link)
17867     {
17868         e->link->link = NULL;
17869         e->link->grabbing = NULL;
17870     }
17871     e->link = NULL;
17872     e->grabbing = NULL;
17873 }
17874 
17875 
17876 
17877 // Link two entities together
ents_link(entity * e1,entity * e2)17878 void ents_link(entity *e1, entity *e2)
17879 {
17880     ent_unlink(e1);
17881     ent_unlink(e2);
17882     e1->grabbing = e2;    // Added for platform layering
17883     e1->link = e2;
17884     e2->link = e1;
17885 }
17886 
17887 
17888 
kill(entity * victim)17889 void kill(entity *victim)
17890 {
17891     int i = 0;
17892     s_collision_attack attack;
17893     entity *tempent = self;
17894 
17895     if(victim == NULL || !victim->exists)
17896     {
17897         return;
17898     }
17899 
17900     execute_onkill_script(victim);
17901 
17902     if((victim->modeldata.type & TYPE_SHOT) && victim->owner && (victim->owner->modeldata.type & TYPE_PLAYER))
17903     {
17904         victim->owner->cantfire = 0;
17905     }
17906 
17907     ent_unlink(victim);
17908     victim->weapent = NULL;
17909     victim->health = 0;
17910     victim->exists = 0;
17911     ent_count--;
17912 
17913     //UT: caution, script function killentity calls this
17914     clear_all_scripts(victim->scripts, 1);
17915 
17916     if(victim->parent && victim->parent->subentity == victim)
17917     {
17918         victim->parent->subentity = NULL;
17919     }
17920     victim->parent = NULL;
17921     if(victim->modeldata.summonkill)
17922     {
17923         attack = emptyattack;
17924         attack.attack_type = max_attack_types;
17925         attack.dropv.y = default_model_dropv.y;
17926         attack.dropv.x = default_model_dropv.x;
17927         attack.dropv.z = default_model_dropv.z;
17928     }
17929     // kill minions
17930     if(victim->modeldata.summonkill == 1 && victim->subentity)
17931     {
17932         // kill only summoned one
17933         victim->subentity->parent = NULL;
17934         self = victim->subentity;
17935         attack.attack_force = self->health;
17936         if(self->takedamage && !level_completed)
17937         {
17938             self->takedamage(self, &attack);
17939         }
17940         else
17941         {
17942             kill(self);
17943         }
17944     }
17945     victim->subentity = NULL;
17946 
17947     for (i = 0; i < MAX_PLAYERS; i++) {
17948         if(victim == player[i].ent) {
17949             player[i].ent = NULL;
17950             break;
17951         }
17952     }
17953 
17954     if(victim == smartbomber)
17955     {
17956         smartbomber = NULL;
17957     }
17958     if(victim == textbox)
17959     {
17960         textbox = NULL;
17961     }
17962 
17963     for(i = 0; i < ent_max; i++)
17964     {
17965         if(ent_list[i]->exists)
17966         {
17967             // kill all minions
17968             self = ent_list[i];
17969             if(self->parent == victim)
17970             {
17971                 self->parent = NULL;
17972                 if(victim->modeldata.summonkill == 2)
17973                 {
17974                     attack.attack_force = self->health;
17975                     if(self->takedamage && !level_completed)
17976                     {
17977                         self->takedamage(self, &attack);
17978                     }
17979                     else
17980                     {
17981                         kill(self);
17982                     }
17983                 }
17984             }
17985             if(self->owner == victim)
17986             {
17987                 self->owner = victim->owner;
17988             }
17989             if(self->opponent == victim)
17990             {
17991                 self->opponent = NULL;
17992             }
17993             if(self->binding.ent == victim)
17994             {
17995                 self->binding.ent = NULL;
17996             }
17997             if(self->landed_on_platform == victim)
17998             {
17999                 self->landed_on_platform = NULL;
18000             }
18001             if(self->hithead == victim)
18002             {
18003                 self->hithead = NULL;
18004             }
18005             if(self->lasthit == victim)
18006             {
18007                 self->lasthit = NULL;
18008             }
18009             if(!textbox && (self->modeldata.type & TYPE_TEXTBOX))
18010             {
18011                 textbox = self;
18012             }
18013         }
18014     }
18015 
18016     self = tempent;
18017 }
18018 
18019 
kill_all()18020 void kill_all()
18021 {
18022     int i;
18023     entity *e = NULL;
18024     for(i = 0; i < ent_max; i++)
18025     {
18026         e = ent_list[i];
18027         if (e && e->exists)
18028         {
18029             execute_onkill_script(e);
18030             clear_all_scripts(e->scripts, 1);
18031         }
18032         e->exists = 0; // well, no need to use kill function
18033     }
18034     textbox = smartbomber = NULL;
18035     time = 0;
18036     ent_count = ent_max = ent_stack_size = 0;
18037     if(ent_list_size > MAX_ENTS) //shrinking...
18038     {
18039         free_ents();
18040         alloc_ents(); //this shouldn't return 0, because the list shrinks...
18041     }
18042 }
18043 
checkhit(entity * attacker,entity * target)18044 int checkhit(entity *attacker, entity *target)
18045 {
18046     #define KEY_ATTACKER    0
18047     #define KEY_TARGET      1
18048     #define KEY_POS_X       0
18049     #define KEY_POS_Y       1
18050     #define KEY_SIZE_X      2
18051     #define KEY_SIZE_Y      3
18052 
18053     s_hitbox *coords_attack;
18054     s_hitbox *coords_detect;
18055     s_collision_attack  *attack = NULL;
18056     s_collision_body    *detect = NULL;
18057     int x1,
18058         x2,
18059         y1,
18060         y2,
18061         attack_instance;
18062     float medx,
18063         medy;
18064     int attack_pos_x    = 0,
18065         attack_pos_y    = 0,
18066         attack_size_x   = 0,
18067         attack_size_y   = 0,
18068         detect_pos_x    = 0,
18069         detect_pos_y    = 0,
18070         detect_size_x   = 0,
18071         detect_size_y   = 0;
18072     int topleast,
18073         bottomleast,
18074         leftleast,
18075         rightleast;
18076     float zdist = 0,
18077         z1 = 0,
18078         z2 = 0;
18079 
18080     if(attacker == target
18081        || !target->animation->collision_body
18082        || !attacker->animation->collision_attack
18083        || !target->animation->vulnerable[target->animpos]
18084        )
18085     {
18086         return 0;
18087     }
18088 
18089     int detect_instance = 0;
18090     int collision_found = 0;
18091 
18092     for(attack_instance = 0; attack_instance < max_collisons; attack_instance++)
18093     {
18094         attack          = attacker->animation->collision_attack[attacker->animpos]->instance[attack_instance];
18095         coords_attack   = attack->coords;
18096 
18097         for(detect_instance = 0; detect_instance < max_collisons; detect_instance++)
18098         {
18099             // Z calculations use increments in,
18100             // each loop, so we need to reset them here.
18101             z1      = attacker->position.z;
18102             z2      = target->position.z;
18103             zdist   = 0;
18104 
18105             //if(!attack->counterattack)
18106             //{
18107                 detect          = target->animation->collision_body[target->animpos]->instance[detect_instance];
18108                 coords_detect   = detect->coords;
18109 
18110             //}
18111             //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)
18112             //{
18113                 //coords_detect = target->animation->collision_attack[target->animpos]->coords;
18114             //}
18115             //else
18116             //{
18117             //    return 0;
18118             //}
18119 
18120             if(coords_attack->z2 > coords_attack->z1)
18121             {
18122                 z1 += coords_attack->z1 + (coords_attack->z2 - coords_attack->z1) / 2;
18123                 zdist = (coords_attack->z2 - coords_attack->z1) / 2;
18124             }
18125             else if(coords_attack->z1)
18126             {
18127                 zdist += coords_attack->z1;
18128             }
18129             else
18130             {
18131                 zdist += attacker->modeldata.grabdistance / 3 + 1;    //temporay fix for integer to float conversion
18132             }
18133 
18134             if(coords_detect->z2 > coords_detect->z1)
18135             {
18136                 z2 += coords_detect->z1 + (coords_detect->z2 - coords_detect->z1) / 2;
18137                 zdist += (coords_detect->z2 - coords_detect->z1) / 2;
18138             }
18139             else if(coords_detect->z1)
18140             {
18141                 zdist += coords_detect->z1;
18142             }
18143 
18144             zdist++; // pass >= <= check
18145 
18146             if(diff(z1, z2) > zdist)
18147             {
18148                 continue;
18149             }
18150 
18151             x1 = (int)(attacker->position.x);
18152             y1 = (int)(z1 - attacker->position.y);
18153             x2 = (int)(target->position.x);
18154             y2 = (int)(z2 - target->position.y);
18155 
18156             if(attacker->direction == DIRECTION_LEFT)
18157             {
18158                 attack_pos_x   = x1 - coords_attack->width;
18159                 attack_pos_y   = y1 + coords_attack->y;
18160                 attack_size_x  = x1 - coords_attack->x;
18161                 attack_size_y  = y1 + coords_attack->height;
18162             }
18163             else
18164             {
18165                 attack_pos_x    = x1 + coords_attack->x;
18166                 attack_pos_y    = y1 + coords_attack->y;
18167                 attack_size_x   = x1 + coords_attack->width;
18168                 attack_size_y   = y1 + coords_attack->height;
18169             }
18170 
18171             if(target->direction == DIRECTION_LEFT)
18172             {
18173                 detect_pos_x    = x2 - coords_detect->width;
18174                 detect_pos_y    = y2 + coords_detect->y;
18175                 detect_size_x   = x2 - coords_detect->x;
18176                 detect_size_y   = y2 + coords_detect->height;
18177             }
18178             else
18179             {
18180                 detect_pos_x    = x2 + coords_detect->x;
18181                 detect_pos_y    = y2 + coords_detect->y;
18182                 detect_size_x   = x2 + coords_detect->width;
18183                 detect_size_y   = y2 + coords_detect->height;
18184             }
18185 
18186             if(attack_pos_x > detect_size_x)
18187             {
18188                 continue;
18189             }
18190             if(detect_pos_x > attack_size_x)
18191             {
18192                 continue;
18193             }
18194             if(attack_pos_y > detect_size_y)
18195             {
18196                 continue;
18197             }
18198             if(detect_pos_y > attack_size_y)
18199             {
18200                 continue;
18201             }
18202 
18203             // If we got this far, set collision flag
18204             // and break this loop.
18205             collision_found = 1;
18206             break;
18207         }
18208 
18209         // If a collision was found
18210         // break out of loop.
18211         if(collision_found)
18212         {
18213             break;
18214         }
18215     }
18216 
18217     if(!collision_found)
18218     {
18219         return 0;
18220     }
18221 
18222     // Find center of attack area
18223     leftleast = attack_pos_x;
18224 
18225     if(leftleast < detect_pos_x)
18226     {
18227         leftleast = detect_pos_x;
18228     }
18229 
18230     topleast = attack_pos_y;
18231 
18232     if(topleast < detect_pos_y)
18233     {
18234         topleast = detect_pos_y;
18235     }
18236 
18237     rightleast = attack_size_x;
18238 
18239     if(rightleast > detect_size_x)
18240     {
18241         rightleast = detect_size_x;
18242     }
18243 
18244     bottomleast = attack_size_y;
18245 
18246     if(bottomleast > detect_size_y)
18247     {
18248         bottomleast = detect_size_y;
18249     }
18250 
18251     medx = (float)(leftleast + rightleast) / 2;
18252     medy = (float)(topleast + bottomleast) / 2;
18253 
18254     // Now convert these coords to 3D
18255     lasthit.position.x = medx;
18256 
18257     if(attacker->position.z > target->position.z)
18258     {
18259         lasthit.position.z = z1 + 1;    // Changed so flashes always spawn in front
18260     }
18261     else
18262     {
18263         lasthit.position.z = z2 + 1;
18264     }
18265 
18266     lasthit.attack      = attack;
18267     lasthit.body        = detect;
18268     lasthit.position.y  = lasthit.position.z - medy;
18269     lasthit.confirm     = 1;
18270 
18271     return 1;
18272 
18273     #undef KEY_ATTACKER
18274     #undef KEY_TARGET
18275     #undef KEY_POS_X
18276     #undef KEY_POS_Y
18277     #undef KEY_SIZE_X
18278     #undef KEY_SIZE_Y
18279 }
18280 
18281 
18282 
18283 /*
18284 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
18285 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
18286 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
18287 within the bottom/top and the left/right area.
18288 */
testhole(int hole,float x,float z)18289 int testhole(int hole, float x, float z)
18290 {
18291     float coef1, coef2;
18292     if(z <= level->holes[hole].z && z >= level->holes[hole].z - level->holes[hole].depth)
18293     {
18294         coef1 = (level->holes[hole].z - z) * ((level->holes[hole].upperleft - level->holes[hole].lowerleft) / level->holes[hole].depth);
18295         coef2 = (level->holes[hole].z - z) * ((level->holes[hole].upperright - level->holes[hole].lowerright) / level->holes[hole].depth);
18296         if(x >= level->holes[hole].x + level->holes[hole].lowerleft + coef1 && x <= level->holes[hole].x + level->holes[hole].lowerright + coef2)
18297         {
18298             return 1;
18299         }
18300     }
18301     return 0;
18302 }
18303 
18304 // find all holes here and return the count
checkholes(float x,float z)18305 int checkholes(float x, float z)
18306 {
18307     int i, c;
18308 
18309     for(i = 0, c = 0; i < level->numholes; i++)
18310     {
18311         c += testhole(i, x, z);
18312     }
18313 
18314     return c;
18315 }
18316 
18317 // find the 1st hole here
checkhole(float x,float z)18318 int checkhole(float x, float z)
18319 {
18320     int i;
18321 
18322     if(level == NULL)
18323     {
18324         return 0;
18325     }
18326 
18327     for(i = 0; i < level->numholes; i++)
18328     {
18329         if(testhole(i, x, z))
18330         {
18331             holez = i;
18332             return 1;
18333         }
18334     }
18335     return 0;
18336 }
18337 
18338 // find all holes here within altitude1 and 2, return the count
checkhole_between(float x,float z,float a1,float a2)18339 int checkhole_between(float x, float z, float a1, float a2)
18340 {
18341     int i, c;
18342 
18343     for(i = 0, c = 0; i < level->numholes; i++)
18344     {
18345         c += (testhole(i, x, z) && level->holes[i].height >= a1 && level->holes[i].height <= a2);
18346     }
18347 
18348     return c;
18349 }
18350 
18351 // get a highest hole below this altitude
checkhole_in(float x,float z,float a)18352 int checkhole_in(float x, float z, float a)
18353 {
18354     float maxa;
18355     int i, ind;
18356 
18357     if(level == NULL)
18358     {
18359         return -1;
18360     }
18361 
18362     maxa = -1;
18363     ind = -1;
18364     for(i = 0; i < level->numholes; i++)
18365     {
18366         if(testhole(i, x, z) && level->holes[i].height+T_WALKOFF >= a && level->holes[i].height > maxa) // && level->holes[i].height+T_WALKOFF >= a
18367         {
18368             maxa = level->holes[i].height;
18369             ind = i;
18370         }
18371     }
18372 
18373     if ( ind >= 0 ) {
18374         holez = ind;
18375         return 1;
18376     } else return 0;
18377 }
18378 
18379 // find the hole id for highest hole
checkholeindex_in(float x,float z,float a)18380 int checkholeindex_in(float x, float z, float a)
18381 {
18382     float maxa;
18383     int i, ind;
18384 
18385     if(level == NULL)
18386     {
18387         return -1;
18388     }
18389 
18390     maxa = -1;
18391     ind = -1;
18392     for(i = 0; i < level->numholes; i++)
18393     {
18394         if(testhole(i, x, z) && level->holes[i].height+T_WALKOFF >= a && level->holes[i].height > maxa) // && level->holes[i].height+T_WALKOFF >= a
18395         {
18396             maxa = level->holes[i].height;
18397             ind = i;
18398         }
18399     }
18400 
18401     return ind;
18402 }
18403 
18404 // find the 1st hole id here
checkhole_index(float x,float z)18405 int checkhole_index(float x, float z)
18406 {
18407     int i;
18408 
18409     if(level == NULL)
18410     {
18411         return -1;
18412     }
18413 
18414     for(i = 0; i < level->numholes; i++)
18415     {
18416         if(testhole(i, x, z))
18417         {
18418             //holez = i;
18419             return i;
18420         }
18421     }
18422     return -1;
18423 }
18424 
18425 /*
18426 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
18427 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
18428 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
18429 within the bottom/top and the left/right area.
18430 */
testwall(int wall,float x,float z)18431 int testwall(int wall, float x, float z)
18432 {
18433     float coef1, coef2;
18434 
18435     //if(wall >= level->numwalls || wall < 0) return 0;
18436     if(z <= level->walls[wall].z && z >= level->walls[wall].z - level->walls[wall].depth)
18437     {
18438         coef1 = (level->walls[wall].z - z) * ((level->walls[wall].upperleft - level->walls[wall].lowerleft) / level->walls[wall].depth);
18439         coef2 = (level->walls[wall].z - z) * ((level->walls[wall].upperright - level->walls[wall].lowerright) / level->walls[wall].depth);
18440         if(x >= level->walls[wall].x + level->walls[wall].lowerleft + coef1 && x <= level->walls[wall].x + level->walls[wall].lowerright + coef2)
18441         {
18442             return 1;
18443         }
18444     }
18445 
18446     return 0;
18447 }
18448 
18449 // find all walls here within altitude1 and 2, return the count
checkwalls(float x,float z,float a1,float a2)18450 int checkwalls(float x, float z, float a1, float a2)
18451 {
18452     int i, c;
18453 
18454     for(i = 0, c = 0; i < level->numwalls; i++)
18455     {
18456         c += (testwall(i, x, z) && level->walls[i].height >= a1 && level->walls[i].height <= a2);
18457     }
18458 
18459     return c;
18460 }
18461 
18462 // get a highest wall below this altitude
checkwall_below(float x,float z,float a)18463 int checkwall_below(float x, float z, float a)
18464 {
18465     float maxa;
18466     int i, ind;
18467 
18468     if(level == NULL)
18469     {
18470         return -1;
18471     }
18472 
18473     maxa = 0;
18474     ind = -1;
18475     for(i = 0; i < level->numwalls; i++)
18476     {
18477         if(testwall(i, x, z) && level->walls[i].height <= a && level->walls[i].height > maxa)
18478         {
18479             maxa = level->walls[i].height;
18480             ind = i;
18481         }
18482     }
18483 
18484     return ind;
18485 }
18486 
18487 // return the 1st wall found here
checkwall(float x,float z)18488 int checkwall(float x, float z)
18489 {
18490     int i;
18491     if(level == NULL)
18492     {
18493         return -1;
18494     }
18495 
18496     for(i = 0; i < level->numwalls; i++)
18497         if(testwall(i, x, z))
18498         {
18499             return i;
18500         }
18501 
18502     return -1;
18503 }
18504 
18505 /*
18506 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
18507 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
18508 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
18509 within the bottom/top and the left/right area.
18510 */
testplatform(entity * plat,float x,float z,entity * exclude)18511 int testplatform(entity *plat, float x, float z, entity *exclude)
18512 {
18513     float coef1, coef2;
18514     float offz, offx;
18515     if(plat == exclude)
18516     {
18517         return 0;
18518     }
18519     if(!plat->animation || !plat->animation->platform || !plat->animation->platform[plat->animpos][7])
18520     {
18521         return 0;
18522     }
18523     offz = plat->position.z + plat->animation->platform[plat->animpos][1];
18524     offx = plat->position.x + plat->animation->platform[plat->animpos][0];
18525     if(z <= offz && z >= offz - plat->animation->platform[plat->animpos][6])
18526     {
18527         coef1 = (offz - z) * ((plat->animation->platform[plat->animpos][2] -
18528                                plat->animation->platform[plat->animpos][3]) / plat->animation->platform[plat->animpos][6]);
18529         coef2 = (offz - z) * ((plat->animation->platform[plat->animpos][4] -
18530                                plat->animation->platform[plat->animpos][5]) / plat->animation->platform[plat->animpos][6]);
18531 
18532         if(x >= offx + plat->animation->platform[plat->animpos][3] + coef1 &&
18533                 x <= offx + plat->animation->platform[plat->animpos][5] + coef2)
18534         {
18535             return 1;
18536         }
18537     }
18538     return 0;
18539 }
18540 
18541 //find the first platform between these 2 altitudes
check_platform_between(float x,float z,float amin,float amax,entity * exclude)18542 entity *check_platform_between(float x, float z, float amin, float amax, entity *exclude)
18543 {
18544     entity *plat = NULL;
18545     int i;
18546 
18547     if(level == NULL)
18548     {
18549         return NULL;
18550     }
18551 
18552     for(i = 0; i < ent_max; i++)
18553     {
18554         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) )
18555         {
18556             plat = ent_list[i];
18557             if(plat->position.y <= amax && plat->position.y + plat->animation->platform[plat->animpos][7] > amin)
18558             {
18559                 return plat;
18560             }
18561         }
18562     }
18563     return NULL;
18564 }
18565 
18566 //find a lowest platform above this altitude
check_platform_above(float x,float z,float a,entity * exclude)18567 entity *check_platform_above(float x, float z, float a, entity *exclude)
18568 {
18569     float mina;
18570     entity *plat = NULL;
18571     int i, ind;
18572 
18573     if(level == NULL)
18574     {
18575         return NULL;
18576     }
18577 
18578     mina = 9999999;
18579     ind = -1;
18580     for(i = 0; i < ent_max; i++)
18581     {
18582         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) )
18583         {
18584             plat = ent_list[i];
18585             if(plat->position.y >= a && plat->position.y < mina)
18586             {
18587                 mina = plat->position.y;
18588                 ind = i;
18589             }
18590         }
18591     }
18592     return (ind >= 0) ? ent_list[ind] : NULL;
18593 }
18594 
18595 //find a highest platform below this altitude
check_platform_below(float x,float z,float a,entity * exclude)18596 entity *check_platform_below(float x, float z, float a, entity *exclude)
18597 {
18598     float maxa;
18599     entity *plat = NULL;
18600     int i, ind;
18601 
18602     if(level == NULL)
18603     {
18604         return NULL;
18605     }
18606 
18607     maxa = MIN_INT;
18608     ind = -1;
18609     for(i = 0; i < ent_max; i++)
18610     {
18611         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) )
18612         {
18613             plat = ent_list[i];
18614             if(plat->position.y + plat->animation->platform[plat->animpos][7] <= a &&
18615                     plat->position.y + plat->animation->platform[plat->animpos][7] > maxa)
18616             {
18617                 maxa = plat->position.y + plat->animation->platform[plat->animpos][7];
18618                 ind = i;
18619             }
18620         }
18621     }
18622     return (ind >= 0) ? ent_list[ind] : NULL;
18623 }
18624 
check_platform_above_entity(entity * e)18625 entity *check_platform_above_entity(entity *e)
18626 {
18627     float mina, heightvar;
18628     entity *plat = NULL;
18629     int i, ind;
18630 
18631     if(level == NULL)
18632     {
18633         return NULL;
18634     }
18635     if(e->animation->size.y)
18636     {
18637         heightvar = e->animation->size.y;
18638     }
18639     else
18640     {
18641         heightvar = e->modeldata.size.y;
18642     }
18643 
18644     mina = 9999999;
18645     ind = -1;
18646     for(i = 0; i < ent_max; i++)
18647     {
18648         if(ent_list[i]->exists && testplatform(ent_list[i], e->position.x, e->position.z, e) )
18649         {
18650             plat = ent_list[i];
18651             if(plat->position.y + plat->animation->platform[plat->animpos][7] > e->position.y + heightvar && plat->position.y < mina)
18652             {
18653                 mina = plat->position.y;
18654                 ind = i;
18655             }
18656         }
18657     }
18658     return (ind >= 0) ? ent_list[ind] : NULL;
18659 }
18660 
check_platform_below_entity(entity * e)18661 entity *check_platform_below_entity(entity *e)
18662 {
18663     float maxa, a;
18664     entity *plat = NULL;
18665     int i, ind;
18666 
18667     if(level == NULL)
18668     {
18669         return NULL;
18670     }
18671 
18672     maxa = MIN_INT;
18673     ind = -1;
18674     for(i = 0; i < ent_max; i++)
18675     {
18676         if(ent_list[i]->exists && testplatform(ent_list[i], e->position.x, e->position.z, e) )
18677         {
18678             plat = ent_list[i];
18679             a = plat->position.y + plat->animation->platform[plat->animpos][7];
18680             if(a < e->position.y + 4 && plat->position.y < e->position.y  && a > maxa)
18681             {
18682                 maxa = a;
18683                 ind = i;
18684             }
18685         }
18686     }
18687     return (ind >= 0) ? ent_list[ind] : NULL;
18688 }
18689 
18690 // find the 1st platform entity here
check_platform(float x,float z,entity * exclude)18691 entity *check_platform(float x, float z, entity *exclude)
18692 {
18693     int i;
18694     if(level == NULL)
18695     {
18696         return NULL;
18697     }
18698 
18699     for(i = 0; i < ent_max; i++)
18700     {
18701         if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude))
18702         {
18703             return ent_list[i];
18704         }
18705     }
18706     return NULL;
18707 }
18708 
get_platform_base(entity * plat)18709 float get_platform_base(entity *plat)
18710 {
18711     float alt = 0;
18712 
18713     alt = plat->position.y;
18714     alt += plat->animation->platform[plat->animpos][7];
18715 
18716     return alt;
18717 }
18718 
is_on_platform(entity * ent)18719 int is_on_platform(entity *ent)
18720 {
18721     entity *plat = check_platform_below(ent->position.x,ent->position.z,ent->position.y+T_WALKOFF,ent);
18722     if (plat)
18723     {
18724         if ( diff(ent->base,get_platform_base(plat)) <= T_WALKOFF ) return 1;
18725     }
18726 
18727     return 0;
18728 }
18729 
get_platform_on(entity * ent)18730 entity *get_platform_on(entity *ent)
18731 {
18732     entity *plat = check_platform_below(ent->position.x,ent->position.z,ent->position.y+T_WALKOFF,ent);
18733     if (plat)
18734     {
18735         if ( diff(ent->base,get_platform_base(plat)) <= T_WALKOFF ) return plat;
18736     }
18737 
18738     return NULL;
18739 }
18740 
18741 // for adjust grab position function, test if an entity can move from a to b
18742 // TODO: check points between the two pionts, if necessary
18743 // special return values:
18744 // -1: ent can jump over walls, for jump check
18745 // -2: for hole ai reaction check
testmove(entity * ent,float sx,float sz,float x,float z)18746 int testmove(entity *ent, float sx, float sz, float x, float z)
18747 {
18748     entity *other = NULL, *platbelow = NULL;
18749     int wall, heightvar;
18750     float xdir, zdir;
18751 
18752     xdir = x - sx;
18753     zdir = z - sz;
18754 
18755     if(!xdir && !zdir)
18756     {
18757         return 1;
18758     }
18759 
18760     // -----------bounds checking---------------
18761     // Subjec to Z and out of bounds? Return to level!
18762     if (ent->modeldata.subject_to_minz > 0)
18763     {
18764         if(zdir && z < PLAYER_MIN_Z)
18765         {
18766             return 0;
18767         }
18768     }
18769 
18770     if (ent->modeldata.subject_to_maxz > 0)
18771     {
18772         if(zdir && z > PLAYER_MAX_Z)
18773         {
18774             return 0;
18775         }
18776     }
18777 
18778     // screen checking
18779     if(ent->modeldata.subject_to_screen > 0)
18780     {
18781         if(x < advancex + 10)
18782         {
18783             return 0;
18784         }
18785         else if(x > advancex + (videomodes.hRes - 10))
18786         {
18787             return 0;
18788         }
18789     }
18790     //-----------end of bounds checking-----------
18791 
18792     //-------------hole checking ---------------------
18793     if(ent->modeldata.subject_to_hole > 0)
18794     {
18795         if(checkhole(x, z) && checkwall(x, z) < 0 && !check_platform_below(x, z, ent->position.y, ent))
18796         {
18797             return -2;
18798         }
18799     }
18800     //-----------end of hole checking---------------
18801 
18802     //--------------obstacle checking ------------------
18803     if(ent->modeldata.subject_to_obstacle > 0)
18804     {
18805         if((other = find_ent_here(ent, x, z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) &&
18806                 (!other->animation->platform || !other->animation->platform[other->animpos][7]))
18807         {
18808             return 0;
18809         }
18810     }
18811     //-----------end of obstacle checking--------------
18812 
18813     // ---------------- platform checking----------------
18814 
18815     if(ent->animation->size.y)
18816     {
18817         heightvar = ent->animation->size.y;
18818     }
18819     else
18820     {
18821         heightvar = ent->modeldata.size.y;
18822     }
18823 
18824     // Check for obstacles with platform code and adjust base accordingly
18825     if(ent->modeldata.subject_to_platform > 0 && (other = check_platform_between(x, z, ent->position.y, ent->position.y + heightvar, ent)) )
18826     {
18827         platbelow = check_platform_below(x, z, ent->position.y+T_WALKOFF, ent);
18828         if ( !platbelow ) return 0;
18829         else
18830         {
18831             float palt = get_platform_base(platbelow);
18832             if ( other != platbelow && diff(ent->position.y,palt) > T_WALKOFF ) return 0;
18833         }
18834     }
18835     //-----------end of platform checking------------------
18836 
18837     // ------------------ wall checking ---------------------
18838     if(ent->modeldata.subject_to_wall > 0 && (wall = checkwall_below(x, z, 999999)) >= 0 && level->walls[wall].height > ent->position.y)
18839     {
18840         if(validanim(ent, ANI_JUMP) && sz < level->walls[wall].z && sz > level->walls[wall].z - level->walls[wall].depth) //Can jump?
18841         {
18842             //rmin = (float)ent->modeldata.animation[ANI_JUMP]->range.min.x;
18843             //rmax = (float)ent->modeldata.animation[ANI_JUMP]->range.max.x;
18844             if(level->walls[wall].height < ent->position.y + ent->modeldata.animation[ANI_JUMP]->range.max.x)
18845             {
18846                 return -1;
18847             }
18848         }
18849         return 0;
18850     }
18851     //----------------end of wall checking--------------
18852 
18853     return 1;
18854 
18855 }
18856 
18857 // find real opponent
set_opponent(entity * ent,entity * other)18858 void set_opponent(entity *ent, entity *other)
18859 {
18860     entity *realself, *realother;
18861 
18862     if(!ent)
18863     {
18864         return;
18865     }
18866 
18867     realself = ent;
18868     while(realself->owner)
18869     {
18870         realself = realself->owner;
18871     }
18872 
18873     realother = other;
18874     while(realother && realother->owner)
18875     {
18876         realother = realother->owner;
18877     }
18878 
18879     realself->opponent = ent->opponent = realother;
18880     if(realother)
18881     {
18882         realother->opponent = other->opponent = realself;
18883     }
18884 
18885 }
18886 
18887 
do_attack(entity * e)18888 void do_attack(entity *e)
18889 {
18890     int them;
18891     int i, t;
18892     int force = 0;
18893     e_blocktype blocktype;
18894     entity *temp            = NULL;
18895     entity *flash           = NULL;    // Used so new flashes can be used
18896     entity *def             = NULL;
18897     entity *topowner        = NULL;
18898     entity *otherowner      = NULL;
18899     entity *target          = NULL;
18900     s_anim      *current_anim;
18901     s_collision_attack *attack = NULL;
18902     int didhit              = 0;
18903     int didblock            = 0;    // So a different sound effect can be played when an attack is blocked
18904     int current_attack_id;
18905     int current_follow_id   = 0;
18906     //int hit_detected        = 0;    // Has a hit been detected?
18907 
18908 
18909 #define followed (current_anim!=e->animation)
18910     static unsigned int new_attack_id = 1;
18911     int fdefense_blockthreshold; //max damage that can be blocked for attack type.
18912 
18913     // Can't get hit after this
18914     if(level_completed)
18915     {
18916         return;
18917     }
18918 
18919     topowner = e; // trace the top owner, for projectile combo checking :)
18920     while(topowner->owner)
18921     {
18922         topowner = topowner->owner;
18923     }
18924 
18925     if(e->projectile > 0)
18926     {
18927         them = e->modeldata.projectilehit;
18928     }
18929     else
18930     {
18931         them = e->modeldata.candamage;
18932     }
18933 
18934     // Every attack gets a unique ID to make sure no one
18935     // gets hit more than once by the same attack
18936     current_attack_id = e->attack_id;
18937 
18938     if(!current_attack_id)
18939     {
18940         ++new_attack_id;
18941         if(new_attack_id == 0)
18942         {
18943             new_attack_id = 1;
18944         }
18945         e->attack_id = current_attack_id = new_attack_id;
18946     }
18947 
18948 
18949     current_anim = e->animation;
18950 
18951     for(i = 0; i < ent_max && !followed; i++)
18952     {
18953         target = ent_list[i];
18954 
18955         if(!target->exists)
18956         {
18957             continue;
18958         }
18959 
18960         // Check collision. If a collision
18961         // is found, lasthit the impacting
18962         // collision pointers are also
18963         // populated into lasthit, which
18964         // we will use below.
18965         if(!checkhit(e, target))
18966         {
18967             continue;
18968         }
18969 
18970         attack = lasthit.attack;
18971         fdefense_blockthreshold = (int)self->defense[attack->attack_type].blockthreshold; //max damage that can be blocked for attack type.
18972         force = attack->attack_force;
18973 
18974         // Verify target is alive.
18975         if(target->dead)
18976         {
18977             continue;
18978         }
18979 
18980         // Verify target is invincible,
18981         // or attack type is an item.
18982         // This is too allow item collection
18983         // even while invincible.
18984         if(target->invincible)
18985         {
18986             if(attack->attack_type != ATK_ITEM)
18987             {
18988                 continue;
18989             }
18990         }
18991 
18992         // If attack is set to only hit
18993         // one entity at a time (attackone),
18994         // we verify last hit (lasthit) is
18995         // set. If last hit is set and
18996         // differs from current target,
18997         // then we are trying to hit
18998         // another entity and should exit.
18999         if(current_anim->attackone > 0)
19000         {
19001             if(e->lasthit)
19002             {
19003                 if(target != e->lasthit)
19004                 {
19005                     continue;
19006                 }
19007             }
19008         }
19009 
19010         // Verify target has a match to
19011         // projectile hit or can damage.
19012         if(!(target->modeldata.type & them))
19013         {
19014             continue;
19015         }
19016 
19017         // Pain time must have expired.
19018         // This is to allow reasonable delay
19019         // between hits so engine will not
19020         // run hit on every update.
19021         if(target->pain_time >= time)
19022         {
19023             continue;
19024         }
19025 
19026         // Target takedamage flag
19027         // must be set.
19028         if(!target->takedamage)
19029         {
19030             continue;
19031         }
19032 
19033         // Attack IDs must be different.
19034         if(target->hit_by_attack_id == current_attack_id && !attack->ignore_attack_id)
19035         {
19036             continue;
19037         }
19038 
19039         // Target laying down? Exit if
19040         // attack only hits standing targets.
19041         if(target->takeaction == common_lie)
19042         {
19043             if(attack->otg == OTG_NONE)
19044             {
19045                 continue;
19046             }
19047         }
19048 
19049         // Target NOT laying down? Exit if
19050         // attack only hits grounded targets.
19051         if(target->takeaction != common_lie)
19052         {
19053             if(attack->otg == OTG_GROUND_ONLY)
19054             {
19055                 continue;
19056             }
19057         }
19058 
19059         // If falling, then check the juggle cost.
19060         if(target->falling == 1)
19061         {
19062             if(attack->jugglecost > target->modeldata.jugglepoints.current)
19063             {
19064                 continue;
19065             }
19066         }
19067 
19068         temp = self;
19069         self = target;
19070 
19071         execute_ondoattack_script(self, e, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, 0, current_attack_id, attack->tag);	//Execute on defender.
19072         execute_ondoattack_script(e, self, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, 1, current_attack_id, attack->tag);	//Execute on attacker.
19073 
19074         // 2010-12-31
19075         // Damon V. Caskey
19076 
19077         // If lasthit.confirm is not true, it must have been turned off by the author; almost
19078         // certainly with the ondoattack event scripts above. Skip the engine's
19079         // default hit handling below. Useful for scripting parry systems, alternate blocking,
19080         // or other custom collision events.
19081         if(lasthit.confirm)
19082         {
19083             didhit = 1;
19084         }
19085         else
19086         {
19087             // By White Dragon
19088             // This line: self = temp; is the fix for
19089             // !lasthit.confirm bug. Without it when
19090             // active lasthitc 0 the damagetaker has
19091             // weird speedy effect.
19092             self = temp;
19093             continue;
19094         }
19095 
19096         otherowner = self; // trace top owner for opponent
19097         while(otherowner->owner)
19098         {
19099             otherowner = otherowner->owner;
19100         }
19101 
19102         //if #01, if they are fired by the same owner, or the owner itself
19103         if(topowner == otherowner)
19104         {
19105             didhit = 0;
19106         }
19107 
19108         //if #02 , ground missle checking, and bullets wont hit each other
19109         if( (e->owner && self->owner) ||
19110                 (e->modeldata.ground && inair(e))  )
19111         {
19112             didhit = 0;
19113         }//end of if #02
19114 
19115         //if #05,   blocking code section
19116         if(didhit)
19117         {
19118             if(attack->attack_type == ATK_ITEM)
19119             {
19120                 execute_didhit_script(e, self, force, attack->attack_drop, self->modeldata.subtype, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, 1, attack->tag);
19121                 didfind_item(e);
19122                 return;
19123             }
19124             //if #051
19125             if(self->toexplode == 1)
19126             {
19127                 self->toexplode = 2;    // Used so the bomb type entity explodes when hit
19128             }
19129             //if #052
19130             if(e->toexplode == 1)
19131             {
19132                 e->toexplode = 2;    // Used so the bomb type entity explodes when hitting
19133             }
19134 
19135             if(inair(self))
19136             {
19137                 self->modeldata.jugglepoints.current = self->modeldata.jugglepoints.current - attack->jugglecost;    //reduce available juggle points.
19138             }
19139 
19140             //if #053
19141             if( !self->modeldata.nopassiveblock && // cant block by itself
19142                     validanim(self, ANI_BLOCK) && // of course, move it here to avoid some useless checking
19143                     ((self->modeldata.guardpoints.max == 0) || (self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current > 0)) &&
19144                     !(self->link ||
19145                       inair(self) ||
19146                       self->frozen ||
19147                       (self->direction == e->direction && self->modeldata.blockback < 1) ||                       // Can't block an attack that is from behind unless blockback flag is enabled
19148                       (!self->idling && self->attacking >= 0)) &&                                                 // Can't block if busy, attack <0 means the character is preparing to attack, he can block during this time
19149                     attack->no_block <= self->defense[attack->attack_type].blockpower &&       // If unblockable, will automatically hit
19150                     (rand32()&self->modeldata.blockodds) == 1 && // Randomly blocks depending on blockodds (1 : blockodds ratio)
19151                     (!self->modeldata.thold || (self->modeldata.thold > 0 && self->modeldata.thold > force)) &&
19152                     (!fdefense_blockthreshold ||                                                                //Specific attack type threshold.
19153                      (fdefense_blockthreshold > force)))
19154             {
19155                 //execute the didhit script
19156                 execute_didhit_script(e, self, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, 1, attack->tag);
19157                 self->takeaction = common_block;
19158                 set_blocking(self);
19159                 self->velocity.x = self->velocity.z = 0;
19160                 ent_set_anim(self, ANI_BLOCK, 0);
19161                 execute_didblock_script(self, e, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, attack->tag);
19162                 if(self->modeldata.guardpoints.max > 0)
19163                 {
19164                     self->modeldata.guardpoints.current = self->modeldata.guardpoints.current - attack->guardcost;
19165                 }
19166                 ++current_anim->animhits;
19167                 didblock = 1;    // Used for when playing the block.wav sound
19168                 // Spawn a flash
19169                 //if #0531
19170                 if(!attack->no_flash)
19171                 {
19172                     if(!self->modeldata.noatflash)
19173                     {
19174                         if(attack->blockflash >= 0)
19175                         {
19176                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, attack->blockflash, NULL);    // custom bflash
19177                         }
19178                         else
19179                         {
19180                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, ent_list[i]->modeldata.bflash, NULL);    // New block flash that can be smaller
19181                         }
19182                     }
19183                     else
19184                     {
19185                         flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, self->modeldata.bflash, NULL);
19186                     }
19187                     //ent_default_init(flash); // initiliaze this because there're no default values now
19188 
19189                     if(flash)
19190                     {
19191                         execute_onspawn_script(flash);
19192                     }
19193                 }
19194                 //end of if #0531
19195             }
19196             else if((self->modeldata.nopassiveblock || self->modeldata.type == TYPE_PLAYER) &&  // can block by itself
19197                     self->blocking &&  // of course he must be blocking
19198                     ((self->modeldata.guardpoints.max == 0) || (self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current > 0)) &&
19199                     !((self->direction == e->direction && self->modeldata.blockback < 1) || self->frozen) &&   // Can't block if facing the wrong direction (unless blockback flag is enabled) or frozen in the block animation or opponent is a projectile
19200                     attack->no_block <= self->defense[attack->attack_type].blockpower &&    // Make sure you are actually blocking and that the attack is blockable
19201                     (!self->modeldata.thold ||
19202                      (self->modeldata.thold > 0 &&
19203                       self->modeldata.thold > force)) &&
19204                     (!self->defense[attack->attack_type].blockthreshold ||                   //Specific attack type threshold.
19205                      (self->defense[attack->attack_type].blockthreshold > force)))
19206             {
19207                 // Only block if the attack is less than the players threshold
19208                 //execute the didhit script
19209                 execute_didhit_script(e, self, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, 1, attack->tag);
19210                 if(self->modeldata.guardpoints.max > 0)
19211                 {
19212                     self->modeldata.guardpoints.current = self->modeldata.guardpoints.current - attack->guardcost;
19213                 }
19214                 ++current_anim->animhits;
19215                 didblock = 1;    // Used for when playing the block.wav sound
19216 
19217                 if(self->modeldata.blockpain && self->modeldata.blockpain <= force && self->animation == self->modeldata.animation[ANI_BLOCK]) //Blockpain 1 and in block animation?
19218                 {
19219                     set_blockpain(self, attack->attack_type, 0);
19220                 }
19221                 execute_didblock_script(self, e, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, attack->tag);
19222 
19223                 // Spawn a flash
19224                 if(!attack->no_flash)
19225                 {
19226                     if(!self->modeldata.noatflash)
19227                     {
19228                         if(attack->blockflash >= 0)
19229                         {
19230                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, attack->blockflash, NULL);    // custom bflash
19231                         }
19232                         else
19233                         {
19234                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, ent_list[i]->modeldata.bflash, NULL);    // New block flash that can be smaller
19235                         }
19236                     }
19237                     else
19238                     {
19239                         flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, self->modeldata.bflash, NULL);
19240                     }
19241                     //ent_default_init(flash); // initiliaze this because there're no default values now
19242                     if(flash)
19243                     {
19244                         execute_onspawn_script(flash);
19245                     }
19246                 }
19247             }
19248             else if(self->animation->counterrange &&	// Has counter range?
19249                     (self->animpos >= self->animation->counterrange->frame.min && self->animpos <= self->animation->counterrange->frame.max) &&  // Current frame within counter range frames?
19250                     !self->frozen &&
19251                     (self->health > force || (self->health-force <= 0 && COUNTERACTION_CONDITION_ALWAYS_RAGE)) &&   // Rage or not?
19252                     // counterrange conditions
19253                     ( (self->animation->counterrange->condition == COUNTERACTION_CONDITION_ALWAYS) || (self->animation->counterrange->condition == COUNTERACTION_CONDITION_ALWAYS_RAGE) ||
19254                     (self->animation->counterrange->condition == COUNTERACTION_CONDITION_HOSTILE && e->modeldata.type & them) ||
19255                     (self->animation->counterrange->condition == COUNTERACTION_CONDITION_HOSTILE_FRONT_NOFREEZE && !attack->no_block && !(self->direction == e->direction) && !attack->freeze ) )
19256 
19257                     ) {
19258 
19259                     // Take damage from attack?
19260                     if(self->animation->counterrange->damaged == COUNTERACTION_DAMAGE_NORMAL)
19261                     {
19262                         if (self->health-force <= 0)
19263                         {
19264                             // White Dragon: commented the alternative method
19265                             /*s_collision_attack atk;
19266 
19267                             atk = emptyattack;
19268                             atk.attack_force = force;
19269                             atk.attack_drop = attack->attack_drop;
19270                             atk.attack_type = attack->attack_type;
19271 
19272                             atk.dropv.y = (float)DEFAULT_ATK_DROPV_Y;
19273                             atk.dropv.x = (float)DEFAULT_ATK_DROPV_X;
19274                             atk.dropv.z = (float)DEFAULT_ATK_DROPV_Z;
19275 
19276                             if (e) self->takedamage(e, &atk);
19277                             else self->takedamage(self, &atk);
19278 
19279                             self = temp;
19280                             return;*/
19281 
19282                             self->health = 1; // rage
19283                         }
19284                         else
19285                         {
19286                             self->health -= force;
19287                         }
19288                     }
19289 
19290                     current_follow_id = animfollows[self->animation->followup.animation - 1];
19291                     if(validanim(self, current_follow_id))
19292                     {
19293                         if(self->modeldata.animation[current_follow_id]->attackone == -1)
19294                         {
19295                             self->modeldata.animation[current_follow_id]->attackone = self->animation->attackone;
19296                         }
19297                         ent_set_anim(self, current_follow_id, 0);
19298                         self->hit_by_attack_id = current_attack_id;
19299                     }
19300 
19301                     if(!attack->no_flash)
19302                     {
19303                         if(!self->modeldata.noatflash)
19304                         {
19305                             if(attack->blockflash >= 0)
19306                             {
19307                                 flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, attack->blockflash, NULL);    // custom bflash
19308                             }
19309                             else
19310                             {
19311                                 flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, ent_list[i]->modeldata.bflash, NULL);    // New block flash that can be smaller
19312                             }
19313                         }
19314                         else
19315                         {
19316                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, self->modeldata.bflash, NULL);
19317                         }
19318                         //ent_default_init(flash); // initiliaze this because there're no default values now
19319                         if(flash)
19320                         {
19321                             execute_onspawn_script(flash);
19322                         }
19323                     }
19324             }
19325             else if(self->takedamage(e, attack))
19326             {
19327                 // Didn't block so go ahead and take the damage
19328                 execute_didhit_script(e, self, force, attack->attack_drop, attack->attack_type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, 0, attack->tag);
19329                 ++e->animation->animhits;
19330 
19331                 e->lasthit = self;
19332 
19333                 // Spawn a flash
19334                 if(!attack->no_flash)
19335                 {
19336                     if(!self->modeldata.noatflash)
19337                     {
19338                         if(attack->hitflash >= 0)
19339                         {
19340                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, attack->hitflash, NULL);
19341                         }
19342                         else
19343                         {
19344                             flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, e->modeldata.flash, NULL);
19345                         }
19346                     }
19347                     else
19348                     {
19349                         flash = spawn(lasthit.position.x, lasthit.position.z, lasthit.position.y, 0, NULL, self->modeldata.flash, NULL);
19350                     }
19351                     if(flash)
19352                     {
19353                         execute_onspawn_script(flash);
19354                     }
19355                 }
19356                 topowner->combotime = time + combodelay; // well, add to its owner's combo
19357 
19358                 if(e->pausetime < time || (inair(e) && !equalairpause))        // if equalairpause is set, inair(e) is nolonger a condition for extra pausetime
19359                 {
19360                     // Adds pause to the current animation
19361                     e->toss_time += attack->pause_add;      // So jump height pauses in midair
19362                     e->nextmove += attack->pause_add;      // xdir, zdir
19363                     e->nextanim += attack->pause_add;       //Pause animation for a bit
19364                     e->nextthink += attack->pause_add;      // So anything that auto moves will pause
19365                     e->pausetime = time + attack->pause_add ; //UT: temporary solution
19366                 }
19367 
19368                 self->toss_time += attack->pause_add;       // So jump height pauses in midair
19369                 self->nextmove += attack->pause_add;      // xdir, zdir
19370                 self->nextanim += attack->pause_add;        //Pause animation for a bit
19371                 self->nextthink += attack->pause_add;       // So anything that auto moves will pause
19372 
19373             }
19374             else
19375             {
19376                 didhit = 0;
19377                 continue;
19378             }
19379             // end of if #053
19380 
19381             // if #054
19382             if(flash)
19383             {
19384                 if(flash->modeldata.toflip)
19385                 {
19386                     flash->direction = (e->position.x > self->position.x);    // Now the flash will flip depending on which side the attacker is on
19387                 }
19388 
19389                 flash->base = lasthit.position.y;
19390                 flash->autokill = 2;
19391             }//end of if #054
19392 
19393             // 2007 3 24, hmm, def should be like this
19394             if(didblock && !def)
19395             {
19396                 def = self;
19397             }
19398             //if #055
19399             if((e->animation->followup.animation) && // follow up?
19400                     (!e->animation->counterrange) && // This isn't suppossed to be a counter, right?
19401                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE) || (self->modeldata.type & e->modeldata.hostile)) &&                                // Does type matter?
19402                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE_NOKILL_NOBLOCK) || ((self->health > 0) && !didblock)) &&                             // check if health or not blocking matters
19403                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE_NOKILL_NOBLOCK_NOGRAB) || ((self->health > 0) && !didblock && cangrab(e, self)) ) && // check if nograb matters
19404                     ((e->animation->followup.condition < FOLLOW_CONDITION_HOSTILE_NOKILL_BLOCK) || ((self->health > 0) && didblock))                                   // check if health or blocking matters
19405               )
19406             {
19407                 current_follow_id = animfollows[e->animation->followup.animation - 1];
19408                 if(validanim(e, current_follow_id))
19409                 {
19410                     if(e->modeldata.animation[current_follow_id]->attackone == -1)
19411                     {
19412                         e->modeldata.animation[current_follow_id]->attackone = e->animation->attackone;
19413                     }
19414                     ent_set_anim(e, current_follow_id, 1);          // Then go to it!
19415                 }
19416                 //followed = 1; // quit loop, animation is changed
19417             }//end of if #055
19418 
19419             self->hit_by_attack_id = current_attack_id;
19420             if(self == def)
19421             {
19422                 self->blocking = didblock;    // yeah, if get hit, stop blocking
19423             }
19424 
19425             //2011/11/24 UT: move the pain_time logic here,
19426             // because block needs this as well otherwise blockratio causes instant death
19427             self->pain_time = time + (attack->pain_time ? attack->pain_time : (GAME_SPEED / 5));
19428             self->nextattack = 0; // reset this, make it easier to fight back
19429         }//end of if #05
19430         self = temp;
19431 
19432     } // end of for
19433 
19434 
19435     // if ###
19436     if(didhit)
19437     {
19438         if(current_anim->energycost)
19439         {
19440             // well, dont check player or not - UTunnels. TODO: take care of that healthcheat
19441             if(e == topowner && current_anim->energycost->cost > 0 && nocost && !healthcheat)
19442             {
19443                 e->tocost = 1;    // Set flag so life is subtracted when animation is finished
19444             }
19445             else if(e != topowner && current_anim->energycost->cost > 0 && nocost && !healthcheat && !e->tocost) // if it is not top, then must be a shot
19446             {
19447                 if(current_anim->energycost->mponly != COST_TYPE_MP_THEN_HP && topowner->mp > 0)
19448                 {
19449                     topowner->mp -= current_anim->energycost->cost;
19450                     if(topowner->mp < 0)
19451                     {
19452                         topowner->mp = 0;
19453                     }
19454                 }
19455                 else
19456                 {
19457                     topowner->health -= current_anim->energycost->cost;
19458                     if(topowner->health <= 0)
19459                     {
19460                         topowner->health = 1;
19461                     }
19462                 }
19463 
19464                 topowner->cantfire = 0;    // Life subtracted, so go ahead and allow firing
19465                 e->tocost = 1;    // Little backwards, but set to 1 so cost doesn't get subtracted multiple times
19466             }
19467         }
19468 
19469         // New blocking checks
19470         //04/27/2008 Damon Caskey: Added checks for defense property specfic blockratio and type. Could probably use some cleaning.
19471         if(didblock && !level->nohurt)
19472         {
19473             if(blockratio || def->defense[attack->attack_type].blockratio) // Is damage reduced?
19474             {
19475                 if (def->defense[attack->attack_type].blockratio)                       //Typed blockratio?
19476                 {
19477                     force = (int)(force * def->defense[attack->attack_type].blockratio);
19478                 }
19479                 else                                                                              //No typed. Use static block ratio.
19480                 {
19481                     force = force / 4;
19482                 }
19483 
19484                 /*
19485                 Block type handling. For backward compatibility we will use BLOCK_TYPE_MP_FIRST regardless
19486                 of defense setting if author has enabled mpblock. Otherwise the defender's blocktype
19487                 for incoming attack type will be used. Once this is determined, we will apply the
19488                 appropriate blocktype accordingly.
19489                 */
19490                 blocktype = mpblock ? BLOCK_TYPE_MP_FIRST : def->defense[attack->attack_type].blocktype;
19491 
19492                 switch (blocktype)
19493                 {
19494                     case BLOCK_TYPE_HP:
19495                         //Do nothing. This is so modders can overidde energycost mponly 1 with health only.
19496                         break;
19497 
19498                     case BLOCK_TYPE_MP_ONLY:
19499 
19500                         def->mp -= force;
19501                         force = 0;
19502 
19503                         if(def->mp < 0)
19504                         {
19505                             def->mp = 0;
19506                         }
19507 
19508                     case BLOCK_TYPE_MP_FIRST:
19509 
19510                         def->mp -= force;
19511 
19512                         /* If there isn't enough MP to cover force, subtract remaining MP from force and set MP to 0 */
19513                         if(def->mp < 0)
19514                         {
19515                             force = -def->mp;
19516                             def->mp = 0;
19517                         }
19518                         else
19519                         {
19520                             force = 0;
19521                         }
19522 
19523                     case BLOCK_TYPE_BOTH:
19524 
19525                         def->mp -= force;
19526 
19527                         if(def->mp < 0)
19528                         {
19529                             def->mp = 0;
19530                         }
19531                 }
19532 
19533                 if(force < def->health)                    // If an attack won't deal damage, this line won't do anything anyway.
19534                 {
19535                     def->health -= force;
19536                 }
19537                 else if(nochipdeath)                       // No chip deaths?
19538                 {
19539                     def->health = 1;
19540                 }
19541                 else
19542                 {
19543                     temp = self;
19544                     self = def;
19545                     self->takedamage(e, attack);           // Must be a fatal attack, then!
19546                     self = temp;
19547                 }
19548             }
19549         }
19550 
19551         if(!didblock)
19552         {
19553             topowner->rush.time = time + (GAME_SPEED * rush[1]);
19554             topowner->rush.count.current++;
19555             if(topowner->rush.count.current > topowner->rush.count.max && topowner->rush.count.current > 1)
19556             {
19557                 topowner->rush.count.max = topowner->rush.count.current;
19558             }
19559         }
19560 
19561         if(didblock)
19562         {
19563             if(attack->blocksound >= 0)
19564             {
19565                 sound_play_sample(attack->blocksound, 0, savedata.effectvol, savedata.effectvol, 100);    // New custom block sound effect
19566             }
19567             else if(SAMPLE_BLOCK >= 0)
19568             {
19569                 sound_play_sample(SAMPLE_BLOCK, 0, savedata.effectvol, savedata.effectvol, 100);    // Default block sound effect
19570             }
19571         }
19572         else if(e->projectile > 0 && SAMPLE_INDIRECT >= 0)
19573         {
19574             sound_play_sample(SAMPLE_INDIRECT, 0, savedata.effectvol, savedata.effectvol, 100);
19575         }
19576         else if(attack->hitsound >= 0)
19577         {
19578             t = 100 - (noslowfx ? 0 : (force - 5));
19579             if(t > 100)
19580             {
19581                 t = 100;
19582             }
19583             else if(t < 60)
19584             {
19585                 t = 60;
19586             }
19587             sound_play_sample(attack->hitsound, 0, savedata.effectvol, savedata.effectvol, t);
19588         }
19589 
19590         if(e->remove_on_attack)
19591         {
19592             kill(e);
19593         }
19594     }//end of if ###
19595 #undef followed
19596 }
19597 
19598 
19599 // it can be useful for next changes
19600 /*static int is_obstacle_around(entity* ent, float threshold)
19601 {
19602     int i, j;
19603     int heightvar;
19604 
19605     if(ent->animation->size.y)
19606     {
19607         heightvar = ent->animation->size.y;
19608     }
19609     else
19610     {
19611         heightvar = ent->modeldata.size.y;
19612     }
19613 
19614     for(i = -1; i <= 1; i++ )
19615     {
19616         for(j = -1; j <= 1; j++ )
19617         {
19618             int w;
19619             entity *hito = find_ent_here(ent, ent->position.x+(i*threshold), ent->position.z+(j*threshold), (TYPE_OBSTACLE | TYPE_TRAP), NULL);
19620             entity *hitp = check_platform_between(ent->position.x+(i*threshold), ent->position.z+(j*threshold), ent->position.y, ent->position.y + heightvar, ent);
19621             int     hitw = (int)( (w = checkwall_below(ent->position.x+(i*threshold), ent->position.z+(j*threshold), 999999)) >= 0 && level->walls[w].height > ent->position.y );
19622 
19623             if ( hito || hitp || hitw ) return 1;
19624         }
19625     }
19626 
19627     return 0;
19628 }*/
19629 
check_gravity(entity * e)19630 void check_gravity(entity *e)
19631 {
19632     int heightvar;
19633     entity *other = NULL, *dust, *tempself, *plat = NULL;
19634     s_collision_attack attack;
19635     float gravity;
19636     float fmin, fmax;
19637 
19638     if(e->update_mark & 8)
19639     {
19640         return;
19641     }
19642 
19643     tempself = self;
19644     self = e;
19645 
19646     adjust_base(self, &plat);
19647 
19648     if(!is_frozen(self) )// Incase an entity is in the air, don't update animations
19649     {
19650         if(self->animation->size.y)
19651         {
19652             heightvar = self->animation->size.y;
19653         }
19654         else
19655         {
19656             heightvar = self->modeldata.size.y;
19657         }
19658 
19659         // White Dragon: turn-off the hitwall flag  if you're not near a obstacle. this help to avoid a hit loop
19660         /*if( (self->position.y <= self->base || !inair(self)) && self->velocity.y <= 0)
19661         {
19662             if ( self->hitwall && !is_obstacle_around(self,1.0) ) self->hitwall = 0;
19663         }*/
19664 
19665         if((self->falling || self->velocity.y || self->position.y != self->base) && self->toss_time <= time)
19666         {
19667             if(heightvar && self->modeldata.subject_to_platform > 0 && self->velocity.y > 0)
19668             {
19669                 other = check_platform_above_entity(self);
19670             }
19671             else
19672             {
19673                 other = NULL;
19674             }
19675 
19676             if( other && other->position.y <= self->position.y + heightvar && !other->modeldata.nohithead)
19677             {
19678                 if(self->hithead == NULL) // bang! Hit the ceiling.
19679                 {
19680                     self->velocity.y = 0;
19681                     self->hithead = other;
19682                     execute_onblocka_script(self, other);
19683                 }
19684             }
19685             else
19686             {
19687                 self->hithead = NULL;
19688             }
19689             // gravity, antigravity factors
19690             self->position.y += self->velocity.y * 100.0 / GAME_SPEED;
19691             if(self->animation->antigrav)
19692             {
19693                 gravity = 0;
19694             }
19695             else
19696             {
19697                 gravity = (level ? level->gravity : default_level_gravity) * (1.0 - self->modeldata.antigravity);
19698             }
19699             if(self->modeldata.subject_to_gravity > 0)
19700             {
19701                 self->velocity.y += gravity * 100.0 / GAME_SPEED;
19702             }
19703 
19704             fmin = (level ? level->maxfallspeed : default_level_maxfallspeed);
19705             fmax = (level ? level->maxtossspeed : default_level_maxtossspeed);
19706 
19707             if(self->velocity.y < fmin)
19708             {
19709                 self->velocity.y = fmin;
19710             }
19711             else if(self->velocity.y > fmax)
19712             {
19713                 self->velocity.y = fmax;
19714             }
19715 
19716             // Dropframe set?
19717             if(self->animation->dropframe)
19718             {
19719                 // If falling and frame has not
19720                 // passed dropframe, set frame to dropframe.
19721                 if(self->velocity.y <= 0 && self->animpos < self->animation->dropframe->frame) // begin dropping
19722                 {
19723                     update_frame(self, self->animation->dropframe->frame);
19724                 }
19725             }
19726 
19727             if (self->velocity.y)
19728             {
19729                 execute_onmovea_script(self);    //Move A event.
19730             }
19731 
19732             if( self->idling && validanim(self, ANI_WALKOFF) && diff(self->position.y, self->base) > T_WALKOFF )
19733             {
19734                 entity *cplat = check_platform_below(self->position.x,self->position.z-1.0,self->position.y,self);
19735 
19736                 // White Dragon: fix for too low velocityz
19737                 if ( !cplat || (cplat && diff(get_platform_base(cplat),self->position.y) > T_WALKOFF) )
19738                 {
19739                     self->idling = 0;
19740                     self->takeaction = common_walkoff;
19741                     ent_set_anim(self, ANI_WALKOFF, 0);
19742                     self->landed_on_platform = plat = NULL;
19743                 }
19744             }
19745 
19746             // UTunnels: tossv <= 0 means land, while >0 means still rising, so
19747             // you wont be stopped if you are passing the edge of a wall
19748             if( (self->position.y <= self->base || !inair(self)) && self->velocity.y <= 0)
19749             {
19750                 self->position.y = self->base;
19751                 self->falling = 0;
19752 
19753                 if ( self->hitwall ) self->hitwall = 0;
19754 
19755                 //self->projectile = 0;
19756                 // cust dust entity
19757                 if(self->modeldata.dust.fall_land >= 0 && self->velocity.y < -1 && self->drop)
19758                 {
19759                     dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.fall_land, NULL);
19760                     if(dust)
19761                     {
19762                         dust->base = self->position.y;
19763                         dust->autokill = 2;
19764                         execute_onspawn_script(dust);
19765                     }
19766                 }
19767                 // bounce/quake
19768                 if(tobounce(self) && self->modeldata.bounce)
19769                 {
19770                     int i;
19771                     self->velocity.x /= self->animation->bounce;
19772                     self->velocity.z /= self->animation->bounce;
19773                     toss(self, (-self->velocity.y) / self->animation->bounce);
19774                     if(level && !(self->modeldata.noquake & NO_QUAKE))
19775                     {
19776                         level->quake = 4;    // Don't shake if specified
19777                     }
19778                     if(SAMPLE_FALL >= 0)
19779                     {
19780                         sound_play_sample(SAMPLE_FALL, 0, savedata.effectvol, savedata.effectvol, 100);
19781                     }
19782                     if(self->modeldata.type & TYPE_PLAYER)
19783                     {
19784                         control_rumble(self->playerindex, 100 * (int)self->velocity.y / 2);
19785                     }
19786                     for(i = 0; i < MAX_PLAYERS; i++)
19787                     {
19788                         control_rumble(i, 75 * (int)self->velocity.y / 2);
19789                     }
19790                 }
19791                 else if((!self->animation->move[self->animpos]->base || self->animation->move[self->animpos]->base < 0) &&
19792                         (!self->animation->move[self->animpos]->y || self->animation->move[self->animpos]->y <= 0))
19793                 {
19794                     if( !(self->modeldata.aimove & AIMOVE1_BOOMERANG) ) self->velocity.x = self->velocity.z = self->velocity.y = 0;
19795                 }
19796                 else
19797                 {
19798                     self->velocity.y = 0;
19799                 }
19800 
19801                 if(plat && !self->landed_on_platform && self->position.y <= plat->position.y + plat->animation->platform[plat->animpos][7])
19802                 {
19803                     self->landed_on_platform = plat;
19804                 }
19805 
19806                 if(self->animation->landframe.frame >= 0                               //Has landframe?
19807                         && self->animation->landframe.frame <= self->animation->numframes  //Not over animation frame count?
19808                         && self->animpos < self->animation->landframe.frame)               //Not already past landframe?
19809                 {
19810                     if(self->animation->landframe.ent >= 0)
19811                     {
19812                         dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->animation->landframe.ent, NULL);
19813                         if(dust)
19814                         {
19815                             dust->base = self->position.y;
19816                             dust->autokill = 2;
19817                             execute_onspawn_script(dust);
19818                         }
19819                     }
19820                     update_frame(self, self->animation->landframe.frame);
19821                 }
19822 
19823                 // takedamage if thrown or basted
19824                 if(self->damage_on_landing > 0 && !self->dead)
19825                 {
19826                     if(self->takedamage)
19827 
19828                     {
19829                         attack              = emptyattack;
19830                         attack.attack_force = self->damage_on_landing;
19831                         attack.attack_type  = ATK_LAND;
19832                         self->takedamage(self, &attack);
19833                     }
19834                     else
19835                     {
19836 
19837                         self->health -= (self->damage_on_landing * self->defense[ATK_LAND].factor);
19838                         if(self->health <= 0 )
19839                         {
19840                             kill(self);
19841                         }
19842                         self->damage_on_landing = 0;
19843                     }
19844                 }
19845                 // in case landing, set hithead to NULL
19846                 self->hithead = NULL;
19847             }// end of if - land checking
19848         }// end of if  - in-air checking
19849         if(self->toss_time <= time)
19850         {
19851             self->toss_time = time + 1;
19852         }
19853 
19854     }//end of if
19855 
19856     self->update_mark |= 8;
19857 
19858     self = tempself;
19859 }
19860 
check_lost()19861 void check_lost()
19862 {
19863     s_collision_attack attack;
19864     int osk = self->modeldata.offscreenkill ? self->modeldata.offscreenkill : DEFAULT_OFFSCREEN_KILL;
19865 
19866     if((self->position.z != 100000 && (advancex - self->position.x > osk || self->position.x - advancex - videomodes.hRes > osk ||
19867                               (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)) ||
19868                               ((level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) && (self->position.z - self->position.y < -osk || self->position.z - self->position.y > videomodes.vRes + osk))		) )
19869             || self->position.y < 2 * PIT_DEPTH) //self->position.z<100000, so weapon item won't be killed
19870     {
19871         if(self->modeldata.type & TYPE_PLAYER)
19872         {
19873             player_die();
19874         }
19875         else
19876         {
19877             kill(self);
19878         }
19879         return;
19880     }
19881 
19882     // fall into a pit
19883     if(self->position.y < PIT_DEPTH)
19884     {
19885         if(!self->takedamage)
19886         {
19887             kill(self);
19888         }
19889         else
19890         {
19891             attack          = emptyattack;
19892             attack.dropv.y = default_model_dropv.y;
19893             attack.dropv.x = default_model_dropv.x;
19894             attack.dropv.z = default_model_dropv.z;
19895             attack.attack_force = self->health;
19896             attack.attack_type  = ATK_PIT;
19897             self->takedamage(self, &attack);
19898         }
19899         return;
19900     }
19901     else if(self->lifespancountdown < 0) //Lifespan expired.
19902     {
19903         if(!self->takedamage)
19904         {
19905             kill(self);
19906         }
19907         else
19908         {
19909             attack          = emptyattack;
19910             attack.dropv.y = default_model_dropv.y;
19911             attack.dropv.x = default_model_dropv.x;
19912             attack.dropv.z = default_model_dropv.z;
19913             attack.attack_force = self->health;
19914             attack.attack_type  = ATK_LIFESPAN;
19915             self->takedamage(self, &attack);
19916         }
19917         return;
19918     }//else
19919 
19920     // Doom count down
19921     if(!is_frozen(self) && self->lifespancountdown != 0x7fffffff)
19922     {
19923         self->lifespancountdown--;
19924     }
19925 }
19926 
19927 // grab walk check
check_link_move(float xdir,float zdir)19928 void check_link_move(float xdir, float zdir)
19929 {
19930     float x, z, gx, gz;
19931     int tryresult;
19932     entity *tempself = self;
19933 
19934     gx = self->grabbing->position.x;
19935     gz = self->grabbing->position.z;
19936     x = self->position.x;
19937     z = self->position.z;
19938     self = self->grabbing;
19939     tryresult = self->trymove(xdir, zdir);
19940     self = tempself;
19941 
19942     if(tryresult != 1) // changed
19943     {
19944         xdir = self->grabbing->position.x - gx;
19945         zdir = self->grabbing->position.z - gz;
19946     }
19947     tryresult = self->trymove(xdir, zdir);
19948     if(tryresult != 1)
19949     {
19950         self->grabbing->position.x = self->position.x - x + gx;
19951         self->grabbing->position.z = self->position.z - z + gz;
19952     }
19953 }
19954 
check_ai()19955 void check_ai()
19956 {
19957     if(self->nextthink <= time && !endgame)
19958     {
19959         self->update_mark |= 2; //mark it
19960         // take actions
19961         if(self->takeaction)
19962         {
19963             self->takeaction();
19964         }
19965 
19966         // A.I. think
19967         if(self->think)
19968         {
19969             if(self->nextthink <= time)
19970             {
19971                 self->nextthink = time + THINK_SPEED;
19972             }
19973             // use noaicontrol flag to turn of A.I. think
19974             if(!self->noaicontrol)
19975             {
19976                 self->think();
19977             }
19978         }
19979 
19980         // Execute think script
19981         execute_think_script(self);
19982 
19983         // Used so all entities can have a spawn animation, and then just changes to the idle animation when done
19984         // move here to so players wont get stuck
19985         if((self->animation == self->modeldata.animation[ANI_SPAWN] || self->animation == self->modeldata.animation[ANI_RESPAWN]) && !self->animating /*&& (!inair(self)||!self->modeldata.subject_to_gravity)*/)
19986         {
19987             set_idle(self);
19988         }
19989     }
19990 }
19991 
check_basemap(int x,int z)19992 float check_basemap(int x, int z)
19993 {
19994     float maxbase = 0, base = -1000;
19995     int i;
19996 
19997     if(!level)
19998     {
19999         return 0;
20000     }
20001 
20002     for(i = 0; i < level->numbasemaps; i++)
20003     {
20004         if(x >= level->basemaps[i].position.x && x < level->basemaps[i].position.x + level->basemaps[i].size.x &&
20005                 z >= level->basemaps[i].position.z && z < level->basemaps[i].position.z + level->basemaps[i].size.z)
20006         {
20007             base = level->basemaps[i].map[x - level->basemaps[i].position.x + level->basemaps[i].size.x * (z - level->basemaps[i].position.z)];
20008             if(base > maxbase)
20009             {
20010                 maxbase = base;
20011             }
20012         }
20013     }
20014     return base == -1000 ? base : maxbase;
20015 }
20016 
check_basemap_index(int x,int z)20017 int check_basemap_index(int x, int z)
20018 {
20019     float maxbase = 0, base = -1000;
20020     int i, index = -1;
20021 
20022     if(!level)
20023     {
20024         return 0;
20025     }
20026 
20027     for(i = 0; i < level->numbasemaps; i++)
20028     {
20029         if(x >= level->basemaps[i].position.x && x < level->basemaps[i].position.x + level->basemaps[i].size.x &&
20030                 z >= level->basemaps[i].position.z && z < level->basemaps[i].position.z + level->basemaps[i].size.z)
20031         {
20032             base = level->basemaps[i].map[x - level->basemaps[i].position.x + level->basemaps[i].size.x * (z - level->basemaps[i].position.z)];
20033             if(base > maxbase)
20034             {
20035                 maxbase = base;
20036                 index = i;
20037             }
20038         }
20039     }
20040     return base == -1000 ? -1 : index;
20041 }
20042 
adjust_base(entity * e,entity ** pla)20043 void adjust_base(entity *e, entity **pla)
20044 {
20045     int wall = -1, hole = -1;
20046     float seta = 0, maxbase = 0;
20047     entity *other = NULL, *plat, *tempself;
20048 
20049     tempself = self;
20050     self = e;
20051 
20052     if(self->velocity.y > 0)
20053     {
20054         self->landed_on_platform = NULL;
20055     }
20056 
20057     if(self->modeldata.subject_to_platform > 0)
20058     {
20059         other = check_platform_below_entity(self);
20060         if(!other && self->landed_on_platform)
20061         {
20062             *pla = self->landed_on_platform = NULL;
20063         }
20064     }
20065     else
20066     {
20067         *pla = other = self->landed_on_platform = NULL;
20068     }
20069 
20070     if(other && !(other->update_mark & 8))
20071     {
20072         check_gravity(other);
20073     }
20074 
20075     //no longer underneath?
20076     if(self->landed_on_platform && !testplatform(self->landed_on_platform, self->position.x, self->position.z, NULL))
20077     {
20078         *pla = self->landed_on_platform = NULL;
20079     }
20080 
20081     if(other && !self->landed_on_platform && self->position.y <= other->position.y + other->animation->platform[other->animpos][7])
20082     {
20083         self->landed_on_platform = other;
20084     }
20085 
20086     if( (plat = self->landed_on_platform) )
20087     {
20088         if(!(plat->update_mark & 8))
20089         {
20090             check_gravity(plat);
20091         }
20092         self->position.y = self->base = plat->position.y + plat->animation->platform[plat->animpos][7];
20093     }
20094 
20095     *pla = other;
20096 
20097     // adjust base
20098     if(self->modeldata.no_adjust_base <= 0)
20099     {
20100         seta = (float)((self->animation->move[self->animpos]->base) ? (self->animation->move[self->animpos]->base) : (-1));
20101 
20102         // Checks to see if entity is over a wall and or obstacle, and adjusts the base accordingly
20103         //wall = checkwall_below(self->position.x, self->position.z);
20104         //find a wall below us
20105         if(self->modeldata.subject_to_wall > 0)
20106         {
20107             wall = checkwall_below(self->position.x, self->position.z, 9999999);
20108         }
20109         else
20110         {
20111             wall = -1;
20112         }
20113 
20114         //printf("stb:%d\n",self->modeldata.subject_to_basemap);
20115         if(self->modeldata.subject_to_basemap > 0) maxbase = check_basemap(self->position.x, self->position.z);
20116 
20117         if(maxbase == -1000 && self->modeldata.subject_to_hole > 0)
20118         {
20119             hole = (wall < 0 && !other) ? checkhole_in(self->position.x, self->position.z, self->position.y) : 0;
20120             if ( hole )
20121             {
20122                 int holeind = checkholeindex_in(self->position.x, self->position.z, self->position.y);
20123                 execute_inhole_script(self, 0, (double)level->holes[holeind].height, holeind);
20124             }
20125 
20126             if(seta < 0 && hole)
20127             {
20128                 self->base = -1000;
20129                 ent_unlink(self);
20130             }
20131             else if(!hole && self->base == -1000)
20132             {
20133                 if(self->position.y >= 0)
20134                 {
20135                     self->base = 0;
20136                 }
20137                 else
20138                 {
20139                     self->velocity.x = self->velocity.z = 0; // hit the hole border
20140                 }
20141             }
20142         }
20143 
20144         if(self->base != -1000 || wall >= 0)
20145         {
20146             if(other != NULL && other != self )
20147             {
20148                 self->base = (seta + self->altbase >= 0 ) * (seta + self->altbase) + (other->position.y + other->animation->platform[other->animpos][7]);
20149             }
20150             else if(wall >= 0)
20151             {
20152                 //self->modeldata.subject_to_wall &&//we move this up to avoid some checking time
20153                 self->base = (seta + self->altbase >= 0 ) * (seta + self->altbase) + level->walls[wall].height;
20154             }
20155             else if(seta >= 0)
20156             {
20157                 self->base = (seta + self->altbase >= 0 ) * (seta + self->altbase);
20158             }
20159             else if(!self->animation->move[self->animpos]->y || self->animation->move[self->animpos]->y == 0)
20160             {
20161                 // No obstacle/wall or seta, so just set to 0
20162                 self->base = 0;
20163             }
20164         }
20165 
20166         if(self->base != -1000 && maxbase > self->base)
20167         {
20168             self->base = maxbase;
20169             // White Dragon: fix bug for floating entity on basemaps using a threshold
20170             if (self->velocity.y <= 0 && self->position.y - self->base <= T_WALKOFF)
20171             {
20172                 self->position.y = self->base;
20173             }
20174             //debug_printf("y:%f maxbase:%f",self->position.y,maxbase);
20175         }
20176     }
20177 
20178     self = tempself;
20179 }
20180 
update_animation()20181 void update_animation()
20182 {
20183     int f;
20184     float scrollv = 0;
20185 
20186     if(level)
20187     {
20188         if(self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT)
20189         {
20190             self->direction = DIRECTION_RIGHT;
20191         }
20192         else if(self->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_LEFT)
20193         {
20194             self->direction = DIRECTION_LEFT;
20195         }
20196         else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT))
20197         {
20198             self->direction = DIRECTION_RIGHT;
20199         }
20200         else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT))
20201         {
20202             self->direction = DIRECTION_LEFT;
20203         }
20204         if(self->modeldata.type & TYPE_PANEL)
20205         {
20206             scrollv += self->modeldata.speed;
20207         }
20208         if(self->modeldata.scroll)
20209         {
20210             scrollv += self->modeldata.scroll;
20211         }
20212         if(scrollv)
20213         {
20214             self->position.x += scrolldx * scrollv;
20215             if(level->scrolldir == SCROLL_UP)
20216             {
20217                 scrollv = -scrollv;
20218             }
20219             self->position.y -= scrolldy * scrollv;
20220             self->base = -99999; // temporary fix otherwise it won't go underground
20221         }
20222     }
20223 
20224     if(!(self->idling & 2) || (self->animnum == ANI_SLEEP && !self->animating))
20225     {
20226         self->sleeptime = time + self->modeldata.sleepwait;
20227     }
20228 
20229     if(self->invincible && time >= self->invinctime)    // Invincible time has run out, turn off
20230     {
20231         self->invincible    = 0;
20232         self->blink         = 0;
20233         self->invinctime    = 0;
20234         self->arrowon       = 0;
20235     }
20236 
20237     if(self->freezetime && time >= self->freezetime)
20238     {
20239         unfrozen(self);
20240     }
20241 
20242     if(self->maptime && time >= self->maptime)
20243     {
20244         ent_set_colourmap(self, self->map);
20245     }
20246 
20247     if(self->sealtime && time >= self->sealtime) //Remove seal, special moves are available again.
20248     {
20249         self->seal = 0;
20250     }
20251     // Reset their escapecount if they aren't being spammed anymore.
20252     if(self->modeldata.escapehits && !self->inpain)
20253     {
20254         self->escapecount = 0;
20255     }
20256 
20257     if(self->nextanim == time ||
20258             ((self->modeldata.type & TYPE_TEXTBOX) && self->modeldata.subtype != SUBTYPE_NOSKIP &&
20259              (bothnewkeys & (FLAG_JUMP | FLAG_ATTACK | FLAG_ATTACK2 | FLAG_ATTACK3 | FLAG_ATTACK4 | FLAG_SPECIAL)))) // Textbox will autoupdate if a valid player presses an action button
20260     {
20261         // Now you can display text and cycle through with any jump/attack/special unless SUBTYPE_NOSKIP
20262 
20263         f = self->animpos + self->animating;
20264 
20265         //Specified loop break frame.
20266         if(self->animation->loop.mode && self->animation->loop.frame.max)
20267         {
20268             if (f == self->animation->loop.frame.max)
20269             {
20270                 if(f < 0)
20271                 {
20272                     f = self->animation->numframes - 1;
20273                 }
20274                 else
20275                 {
20276                     f = 0;
20277                 }
20278 
20279                 if (self->animation->loop.frame.min)
20280                 {
20281                     f = self->animation->loop.frame.min;
20282                 }
20283             }
20284             else if((unsigned)f >= (unsigned)self->animation->numframes)
20285             {
20286                 self->animating = 0;
20287 
20288                 if(self->autokill)
20289                 {
20290                     kill(self);
20291                     return;
20292                 }
20293             }
20294         }
20295         else if((unsigned)f >= (unsigned)self->animation->numframes)
20296         {
20297             if(f < 0)
20298             {
20299                 f = self->animation->numframes - 1;
20300             }
20301             else
20302             {
20303                 f = 0;
20304             }
20305 
20306             if(!self->animation->loop.mode)
20307             {
20308                 self->animating = 0;
20309 
20310                 if(self->autokill)
20311                 {
20312                     kill(self);
20313                     return;
20314                 }
20315             }
20316             else
20317             {
20318                 if (self->animation->loop.frame.min)
20319                 {
20320                     f = self->animation->loop.frame.min;
20321                 }
20322             }
20323         }
20324 
20325         if(self->animating)
20326         {
20327             //self->nextanim = time + (self->animation->delay[f]);
20328             self->update_mark |= 1; // frame updated, mark it
20329             // just switch frame to f, if frozen, expand_time will deal with it well
20330             update_frame(self, f);
20331         }
20332     }
20333 
20334 }
20335 
check_attack()20336 void check_attack()
20337 {
20338     // a normal fall
20339     if(self->falling && !self->projectile)
20340     {
20341         self->attack_id = 0;
20342         return;
20343     }
20344     // on ground
20345     if(self->drop && !self->falling)
20346     {
20347         self->attack_id = 0;
20348         return;
20349     }
20350 
20351     // Can't hit an opponent if you are frozen
20352     if(!is_frozen(self) && self->animation->collision_attack &&
20353             self->animation->collision_attack[self->animpos])
20354     {
20355                 do_attack(self);
20356         return;
20357     }
20358     self->attack_id = 0;
20359 }
20360 
20361 
update_health()20362 void update_health()
20363 {
20364     //12/30/2008: Guardrate by OX. Guardpoints increase over time.
20365     if(self->modeldata.guardpoints.max > 0 && time >= self->guardtime) // If this is > 0 then guardpoints are set..
20366     {
20367         if(self->blocking)
20368         {
20369             self->modeldata.guardpoints.current += (self->modeldata.guardrate / 2);
20370             if(self->modeldata.guardpoints.current > self->modeldata.guardpoints.max)
20371             {
20372                 self->modeldata.guardpoints.current = self->modeldata.guardpoints.max;
20373             }
20374         }
20375         else
20376         {
20377             self->modeldata.guardpoints.current += self->modeldata.guardrate;
20378             if(self->modeldata.guardpoints.current > self->modeldata.guardpoints.max)
20379             {
20380                 self->modeldata.guardpoints.current = self->modeldata.guardpoints.max;
20381             }
20382         }
20383         self->guardtime = time + GAME_SPEED;    //Reset guardtime.
20384     }
20385 
20386     common_dot();   //Damage over time.
20387 
20388     // this is for restoring mp by time by tails
20389     // Cleaning and addition of mpstable by DC, 08172008.
20390     // stabletype 4 added by OX 12272008
20391     if(magic_type == 0 && !self->charging)
20392     {
20393         if(time >= self->magictime)
20394         {
20395 
20396             // 1 Only recover MP > mpstableval.
20397             // 2 No recover. Drop MP if MP < mpstableval.
20398             // 3 Both: recover if MP if MP < mpstableval and drop if MP > mpstableval.
20399             // 0 Default. Recover MP at all times.
20400 
20401 
20402             if (self->modeldata.mpstable == 1)
20403             {
20404                 if (self->mp < self->modeldata.mpstableval)
20405                 {
20406                     self->mp += self->modeldata.mprate;
20407                 }
20408             }
20409             else if(self->modeldata.mpstable == 2)
20410             {
20411                 if (self->mp > self->modeldata.mpstableval)
20412                 {
20413                     self->mp -= self->modeldata.mpdroprate;
20414                 }
20415             }
20416             else if (self->modeldata.mpstable == 3)
20417             {
20418                 if (self->mp < self->modeldata.mpstableval)
20419                 {
20420 
20421                     self->mp += self->modeldata.mprate;
20422                 }
20423                 else if (self->mp > self->modeldata.mpstableval)
20424                 {
20425                     self->mp -= self->modeldata.mpdroprate;
20426                 }
20427             }
20428 
20429             // OX. Stabletype 4. Gain mp until it reaches max. Then it drops down to mpstableval.
20430             else if (self->modeldata.mpstable == 4)
20431             {
20432                 if(self->mp <= self->modeldata.mpstableval)
20433                 {
20434                     self->modeldata.mpswitch = 0;
20435                 }
20436                 else if(self->mp == self->modeldata.mp)
20437                 {
20438                     self->modeldata.mpswitch = 1;
20439                 }
20440 
20441                 if(self->modeldata.mpswitch == 1)
20442                 {
20443                     self->mp -= self->modeldata.mpdroprate;
20444                 }
20445                 else if(self->modeldata.mpswitch == 0)
20446                 {
20447                     self->mp += self->modeldata.mprate;
20448                 }
20449             }
20450             else
20451             {
20452                 self->mp += self->modeldata.mprate;
20453             }
20454 
20455             self->magictime = time + GAME_SPEED;    //Reset magictime.
20456         }
20457     }
20458     if(self->charging && time >= self->mpchargetime)
20459     {
20460         self->mp += self->modeldata.chargerate;
20461         self->mpchargetime = time + (GAME_SPEED / 4);
20462     }
20463     if(self->mp > self->modeldata.mp)
20464     {
20465         self->mp = self->modeldata.mp;    // Don't want to add more than the max
20466     }
20467 
20468     if(self->oldhealth < self->health)
20469     {
20470         self->oldhealth++;
20471     }
20472     else if(self->oldhealth > self->health)
20473     {
20474         self->oldhealth--;
20475     }
20476 
20477     if(self->oldmp < self->mp)
20478     {
20479         self->oldmp++;
20480     }
20481     else if(self->oldmp > self->mp)
20482     {
20483         self->oldmp--;
20484     }
20485 }
20486 
common_dot()20487 void common_dot()
20488 {
20489     //common_dot
20490     //Damon V. Caskey
20491     //06172009
20492     //Mitigates damage over time (dot). Moved here from update_health().
20493 
20494     int         iFForce;    //Final force; total damage after defense and offense factors are applied.
20495     int         iType;      //Attack type.
20496     int         iIndex;     //Dot index.
20497     e_dot_mode  iDot;       //Dot mode.
20498     int         iDot_time;  //Dot expire time.
20499     int         iDot_cnt;   //Dot next tick time.
20500     int         iDot_rate;  //Dot tick rate.
20501     int         iForce;     //Unmodified force.
20502     float       fOffense;   //Owner's offense.
20503     float       fDefense;   //Self defense.
20504     entity     *eOpp;       //Owner of dot effect.
20505     s_collision_attack    attack;     //Attack struct.
20506 
20507     for(iIndex = 0; iIndex < MAX_DOTS; iIndex++)                                                //Loop through all DOT indexes.
20508     {
20509         iDot_time   =   self->dot_time[iIndex];                                                 //Get expire time.
20510         iDot_cnt    =   self->dot_cnt[iIndex];                                                  //Get next tick time.
20511         iDot_rate   =   self->dot_rate[iIndex];                                                 //Get tick rate.
20512 
20513         if(iDot_time)                                                                           //Dot time present?
20514         {
20515             if(time > iDot_time)                                                                //Dot effect expired? Then clear variants.
20516             {
20517                 self->dot[iIndex]       = 0;
20518                 self->dot_atk[iIndex]   = 0;
20519                 self->dot_cnt[iIndex]   = 0;
20520                 self->dot_rate[iIndex]  = 0;
20521                 self->dot_time[iIndex]  = 0;
20522                 self->dot_force[iIndex] = 0;
20523             }
20524             else if(time >= iDot_cnt && self->health >= 0)                                      //Time for a dot tick and alive?
20525             {
20526                 self->dot_cnt[iIndex] = time + (iDot_rate * GAME_SPEED / 100);                  //Reset next tick time.
20527 
20528                 iDot    =   self->dot[iIndex];                                                  //Get dot mode.
20529                 iForce  =   self->dot_force[iIndex];                                            //Get dot force.
20530 
20531                 if(iDot == DOT_MODE_HP
20532                     || iDot == DOT_MODE_HP_MP
20533                     || iDot == DOT_MODE_NON_LETHAL_HP
20534                     || iDot == DOT_MODE_NON_LETHAL_HP_MP)                            //HP?
20535                 {
20536                     eOpp        = self->dot_owner[iIndex];                                      //Get dot effect owner.
20537                     iType       = self->dot_atk[iIndex];                                        //Get attack type.
20538                     iFForce     = iForce;                                                       //Initialize final force.
20539                     fOffense    = eOpp->offense_factors[iType];									//Get owner's offense.
20540                     fDefense    = self->defense[iType].factor;									//Get Self defense.
20541 
20542                     if (fOffense)
20543                     {
20544                         iFForce = (int)(iForce  * fOffense);       //Apply offense factors.
20545                     }
20546                     if (fDefense)
20547                     {
20548                         iFForce = (int)(iFForce * fDefense);       //Apply defense factors.
20549                     }
20550 
20551                     if(iFForce >= self->health && (iDot == 4 || iDot == 5))                     //Total force lethal?
20552                     {
20553                         attack              = emptyattack;                                      //Clear struct.
20554                         attack.attack_type  = iType;                                            //Set type.
20555                         attack.attack_force = iForce;                                           //Set force. Use unmodified force here; takedamage applys damage mitigation.
20556                         attack.dropv.y     = default_model_dropv.y;                           //Apply drop Y.
20557                         attack.dropv.x     = default_model_dropv.x;                           //Apply drop X
20558                         attack.dropv.z     = default_model_dropv.z;                           //Apply drop Z
20559 
20560                         if(self->takedamage)                                                    //Defender uses takedamage()?
20561                         {
20562                             self->takedamage(eOpp, &attack);                                    //Apply attack to kill defender.
20563                         }
20564                         else
20565                         {
20566                             kill(self);                                                         //Kill defender instantly.
20567                         }
20568                     }
20569                     else                                                                        //Total force less then health or using non lethal setting.
20570                     {
20571                         if (self->health > iFForce)                                             //Final force less then health?
20572                         {
20573                             self->health -= iFForce;                                            //Reduce health directly. Using takedamage() breaks grabs and spams defender's status in HUD.
20574                         }
20575                         else
20576                         {
20577                             self->health = 1;                                                   //Set min health.
20578                         }
20579                         execute_takedamage_script(self, eOpp, iForce, 0, iType, 0, 0, 0, 0, 0);    //Execute the takedamage script.
20580                     }
20581                 }
20582 
20583                 /* Drain MP ? */
20584                 if(iDot == DOT_MODE_MP
20585                    || iDot == DOT_MODE_HP_MP
20586                    || iDot == DOT_MODE_NON_LETHAL_HP_MP)
20587                 {
20588                     self->mp -= iForce;                                                         //Subtract force from MP.
20589                     if(self->mp < 0)
20590                     {
20591                         self->mp = 0;    //Stablize MP at 0.
20592                     }
20593                 }
20594             }
20595         }
20596     }
20597 }
20598 
adjust_bind(entity * e)20599 void adjust_bind(entity *e)
20600 {
20601     if(e->binding.ent)
20602     {
20603         if(e->binding.ani_bind)
20604         {
20605             if(e->animnum != e->binding.ent->animnum)
20606             {
20607                 if(!validanim(e, e->binding.ent->animnum))
20608                 {
20609                     if(e->binding.ani_bind & 4)
20610                     {
20611                         kill(e);
20612                     }
20613                     e->binding.ent = NULL;
20614                     return;
20615                 }
20616                 ent_set_anim(e, e->binding.ent->animnum, 1);
20617             }
20618             if(e->animpos != e->binding.ent->animpos && e->binding.ani_bind & 2)
20619             {
20620                 update_frame(e, e->binding.ent->animpos);
20621             }
20622         }
20623         if (e->binding.offset_flag.z) e->position.z = e->binding.ent->position.z + e->binding.offset.z;
20624         if (e->binding.offset_flag.y) e->position.y = e->binding.ent->position.y + e->binding.offset.y;
20625         e->sortid = e->binding.ent->sortid + e->binding.sortid;
20626 
20627         switch(e->binding.direction)
20628         {
20629         case DIRECTION_ADJUST_NONE:
20630             if(e->binding.ent->direction == DIRECTION_RIGHT)
20631             {
20632                 if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x + e->binding.offset.x;
20633             }
20634             else
20635             {
20636                 if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x - e->binding.offset.x;
20637             }
20638             break;
20639         case DIRECTION_ADJUST_SAME:
20640             e->direction = e->binding.ent->direction;
20641             if(e->binding.ent->direction == DIRECTION_RIGHT)
20642             {
20643                 if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x + e->binding.offset.x;
20644             }
20645             else
20646             {
20647                 if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x - e->binding.offset.x;
20648             }
20649             break;
20650         case DIRECTION_ADJUST_OPPOSITE:
20651             e->direction = !e->binding.ent->direction;
20652             if(e->binding.ent->direction == DIRECTION_RIGHT)
20653             {
20654                 if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x + e->binding.offset.x;
20655             }
20656             else
20657             {
20658                 if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x - e->binding.offset.x;
20659             }
20660             break;
20661         case DIRECTION_ADJUST_RIGHT:
20662             e->direction = DIRECTION_RIGHT;
20663             if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x + e->binding.offset.x;
20664             break;
20665         case DIRECTION_ADJUST_LEFT:
20666             e->direction = DIRECTION_LEFT;
20667             if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x + e->binding.offset.x;
20668             break;
20669         default:
20670             if (e->binding.offset_flag.x) e->position.x = e->binding.ent->position.x + e->binding.offset.x;
20671             break;
20672             // the default is no change :), just give a value of 12345 or so
20673         }
20674     }
20675 }
20676 
20677 
check_move(entity * e)20678 void check_move(entity *e)
20679 {
20680     float x, z;
20681     entity *plat, *tempself;
20682 
20683     if((e->update_mark & 4))
20684     {
20685         return;
20686     }
20687     tempself = self;
20688     self = e;
20689 
20690     x = self->position.x;
20691     z = self->position.z;
20692 
20693     // check moving platform
20694     if((plat = self->landed_on_platform) ) // on the platform?
20695     {
20696         //update platform first to get actual movex and movez
20697         if(!(plat->update_mark & 4))
20698         {
20699             check_move(plat);
20700         }
20701         if(plat->movex || plat->movez)
20702         {
20703             if(self->trymove)
20704             {
20705                 if(self->grabbing)
20706                 {
20707                     check_link_move(plat->movex, plat->movez);
20708                 }
20709                 else if(!self->link)
20710                 {
20711                     self->trymove(plat->movex, plat->movez);
20712                 }
20713             }
20714             else
20715             {
20716                 self->position.x += plat->movex;
20717                 self->position.z += plat->movez;
20718             }
20719         }
20720     }
20721     if(!is_frozen(self) )
20722     {
20723         if(self->nextmove <= time && (self->movex || self->movez) )
20724         {
20725             if(self->trymove)
20726             {
20727                 // only do linked move while grabwalking for now,
20728                 // otherwise some grab moves that use move/movez command may act strangely
20729                 if(self->grabbing && self->grabwalking)
20730                 {
20731                     check_link_move(self->movex, self->movez);
20732                 }
20733                 else// if(!self->link || self->grabbing)
20734                 {
20735                     if(1 != self->trymove(self->movex, self->movez) && self->idling)
20736                     {
20737                         self->pathblocked += time % 2;
20738                     }
20739                     else
20740                     {
20741                         self->pathblocked = 0;
20742                     }
20743                 }
20744             }
20745             else
20746             {
20747                 self->position.x += self->movex;
20748                 self->position.z += self->movez;
20749             }
20750         }
20751         if(self->nextmove <= time)
20752         {
20753             self->nextmove = time + 1;
20754         }
20755 
20756     }
20757     self->movex = self->position.x - x;
20758     self->movez = self->position.z - z;
20759     self->update_mark |= 4;
20760     self = tempself;
20761 }
20762 
ent_post_update(entity * e)20763 void ent_post_update(entity *e)
20764 {
20765     check_gravity(e);// check gravity
20766     check_move(e);
20767 
20768     adjust_bind(e);
20769 }
20770 
20771 // arrenge the list reduce its length
arrange_ents()20772 void arrange_ents()
20773 {
20774     int i, ind = -1;
20775     entity *temp;
20776     if(ent_count == 0)
20777     {
20778         return;
20779     }
20780     if(ent_max == ent_count)
20781     {
20782         for(i = 0; i < ent_max; i++)
20783         {
20784             if(ent_list[i]->exists)
20785             {
20786                 ent_post_update(ent_list[i]);
20787             }
20788         }
20789     }
20790     else
20791     {
20792         for(i = 0; i < ent_max; i++)
20793         {
20794             if(!ent_list[i]->exists && ind < 0)
20795             {
20796                 ind = i;
20797             }
20798             else if(ent_list[i]->exists && ind >= 0)
20799             {
20800                 temp = ent_list[i];
20801                 ent_list[i] = ent_list[ind];
20802                 ent_list[ind] = temp;
20803                 ind++;
20804             }
20805             if(ent_list[i]->exists)
20806             {
20807                 ent_post_update(ent_list[i]);
20808             }
20809         }
20810         ent_max = ent_count;
20811     }
20812     for(i = 0; i < ent_max; i++)
20813     {
20814         ent_list[i]->movex = ent_list[i]->movez = 0;
20815     }
20816 }
20817 
20818 // Update all entities that wish to think or animate in this cycle.
20819 // All loops are separated because "self" might die during a pass.
update_ents()20820 void update_ents()
20821 {
20822     int i;
20823     for(i = 0; i < ent_max; i++)
20824     {
20825         if(ent_list[i]->exists && time != ent_list[i]->timestamp)// dont update fresh entity
20826         {
20827             self = ent_list[i];
20828             self->update_mark = 0;
20829             if(level)
20830             {
20831                 check_lost();    // check lost caused by level scrolling or lifespan
20832             }
20833             if(!self->exists)
20834             {
20835                 continue;
20836             }
20837             // expand time incase being frozen
20838             if(is_frozen(self))
20839             {
20840                 expand_time(self);
20841             }
20842             else
20843             {
20844                 execute_updateentity_script(self);// execute a script
20845                 if(!self->exists)
20846                 {
20847                     continue;
20848                 }
20849                 check_ai();// check ai
20850                 if(!self->exists)
20851                 {
20852                     continue;
20853                 }
20854                 update_animation(); // if not frozen, update animation
20855                 if(!self->exists)
20856                 {
20857                     continue;
20858                 }
20859                 check_attack();// Collission detection
20860                 if(!self->exists)
20861                 {
20862                     continue;
20863                 }
20864                 update_health();// Update displayed health
20865                 self->movex += self->velocity.x * self->speedmul * (100.0 / GAME_SPEED);
20866                 self->movez += self->velocity.z * self->speedmul * (100.0 / GAME_SPEED);
20867             }
20868         }
20869     }//end of for
20870     arrange_ents();
20871     /*
20872     if(time>=nextplan){
20873     	plan();
20874     	nextplan = time+GAME_SPEED/2;
20875     }*/
20876 }
20877 
20878 
display_ents()20879 void display_ents()
20880 {
20881     unsigned f;
20882     int i, z, wall = 0, wall2;
20883     entity *e = NULL;
20884     entity *other = NULL;
20885     int qx, qy, sy, sz, alty;
20886     int sortid;
20887     float basemap;
20888     float temp1, temp2;
20889     int useshadow = 0;
20890     int can_mirror = 0;
20891     int shadowz;
20892     s_drawmethod *drawmethod = NULL;
20893     s_drawmethod commonmethod;
20894     s_drawmethod shadowmethod;
20895     int use_mirror = (level && level->mirror);
20896 
20897     int o_scrx = screenx, o_scry = screeny, scrx, scry;
20898 
20899     if(level)
20900     {
20901         shadowz = SHADOW_Z;
20902     }
20903     else
20904     {
20905         shadowz = MIN_INT + 100;
20906     }
20907 
20908     for(i = 0; i < ent_max; i++)
20909     {
20910         if(ent_list[i] && ent_list[i]->exists)
20911         {
20912             e = ent_list[i];
20913             // in a loop need to initialize to reset prev val. to avoid bugs.
20914             z = basemap = qx = qy = sy = sz = alty = sortid = temp1 = temp2 = 0;
20915 
20916             if(Script_IsInitialized(e->scripts->ondraw_script))
20917             {
20918                 ent_stack[ent_stack_size++] = e;
20919             }
20920             //if(!e->exists) continue; // just in case kill is called in the script
20921 
20922             if(e->modeldata.hpbarstatus.size.x)
20923             {
20924                 drawenemystatus(e);
20925 
20926             }
20927             sortid = e->sortid;
20928             scrx = o_scrx - ((e->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_x_offset);
20929             scry = o_scry - ((e->modeldata.noquake & NO_QUAKEN) ? 0 : gfx_y_offset);
20930             if(freezeall || !(e->blink && (time % (GAME_SPEED / 10)) < (GAME_SPEED / 20)))
20931             {
20932                 float eheight = T_WALKOFF, eplatheight = 0;
20933 
20934                 // get the height of the entity
20935                 if ( e->animation->platform )
20936                 {
20937                     s_anim *anim = e->animation;
20938 
20939                     if ( anim->platform[e->animpos] )
20940                     {
20941                         if ( anim->platform[e->animpos][7] ) eplatheight += anim->platform[e->animpos][7];
20942                     }
20943                 }
20944                 if ( e->modeldata.size.y && eplatheight <= 0 ) eheight += e->modeldata.size.y;
20945                 else eheight += eplatheight;
20946 
20947                 // If special is being executed, display all entities regardless
20948                 f = e->animation->sprite[e->animpos];
20949 
20950                 //other = check_platform(e->position.x, e->position.z, e);
20951                 other = check_platform_below(e->position.x, e->position.z, e->position.y+eheight, e);
20952                 wall = checkwall(e->position.x, e->position.z);
20953 
20954                 if(e->modeldata.subject_to_basemap > 0) basemap = check_basemap(e->position.x, e->position.z);
20955 
20956                 if(f < sprites_loaded)
20957                 {
20958                     // var "z" takes into account whether it has a setlayer set, whether there are other entities on
20959                     // the same "z", in which case there is a layer offset, whether the entity is on an obstacle, and
20960                     // whether the entity is grabbing someone and has grabback set
20961 
20962                     z = (int)e->position.z;    // Set the layer offset
20963 
20964                     /*if(e->binding.ent)
20965                     {
20966                         sortid = e->binding.ent->sortid + e->binding.sortid;
20967                     }*/
20968 
20969                     if(e->grabbing && e->modeldata.grabback)
20970                     {
20971                         sortid = e->link->sortid - 1;    // Grab animation displayed behind
20972                     }
20973                     else if(!e->modeldata.grabback && e->grabbing)
20974                     {
20975                         sortid = e->link->sortid + 1;
20976                     }
20977                     /*
20978                     					if(e->binding.ent && e->binding.ent->grabbing==e)
20979                     					{
20980                     						if(e->binding.ent->modeldata.grabback) z--;
20981                     						else                             z++;
20982                     					}
20983                     */
20984                     if(other && e->position.y >= other->position.y + other->animation->platform[other->animpos][7] && !other->modeldata.setlayer)
20985                     {
20986                         if(
20987                             e->link &&
20988                             ((e->modeldata.grabback &&
20989                               !e->grabbing) ||
20990                              (e->link->modeldata.grabback &&
20991                               e->link->grabbing) ||
20992                              e->grabbing)
20993                         )
20994                         {
20995                             float zdepth = (float)( (float)e->position.z - (float)other->position.z + (float)other->animation->platform[other->animpos][6] - (float)other->animation->platform[other->animpos][1] );
20996 
20997                             // Make sure entities get displayed in front of obstacle and grabbee
20998                             sortid = other->sortid + zdepth + 2;
20999                             e->sortid = sortid;
21000                             z = (int)( other->position.z + 2 );
21001                         }
21002 
21003                         else
21004                         {
21005                             float zdepth = (float)( (float)e->position.z - (float)other->position.z + (float)other->animation->platform[other->animpos][6] - (float)other->animation->platform[other->animpos][1] );
21006                             //if ( e->model->type == TYPE_PLAYER ) debug_printf("zdepth: %f",zdepth);
21007 
21008                             // Entity should always display in front of the obstacle
21009                             sortid = other->sortid + zdepth + 1;
21010                             e->sortid = sortid;
21011                             z = (int)( other->position.z + 1 );
21012                         }
21013 
21014                     }
21015 
21016                     if(e->owner)
21017                     {
21018                         if ( !(self->modeldata.aimove & AIMOVE1_BOOMERANG) ) sortid = e->owner->sortid + 1;    // Always in front
21019                     }
21020 
21021                     if(e->modeldata.setlayer)
21022                     {
21023                         z = HOLE_Z + e->modeldata.setlayer;    // Setlayer takes precedence
21024                     }
21025 
21026                     //UT: commented this out, it seems to be some legacy code, ==2 doesn't make sense anymore
21027                     //if(checkhole(e->position.x, e->position.z)==2) z = PANEL_Z-1;        // place behind panels
21028 
21029                     drawmethod = e->animation->drawmethods ? getDrawMethod(e->animation, e->animpos) : NULL;
21030                     //drawmethod = e->animation->drawmethods?e->animation->drawmethods[e->animpos]:NULL;
21031                     if(e->drawmethod.flag)
21032                     {
21033                         drawmethod = &(e->drawmethod);
21034                     }
21035                     if(!drawmethod)
21036                     {
21037                         commonmethod = plainmethod;
21038                     }
21039                     else
21040                     {
21041                         commonmethod = *drawmethod;
21042                     }
21043                     drawmethod = &commonmethod;
21044 
21045                     if(e->modeldata.alpha >= 1 && e->modeldata.alpha <= MAX_BLENDINGS)
21046                     {
21047                         if(drawmethod->alpha < 0)
21048                         {
21049                             drawmethod->alpha = e->modeldata.alpha;
21050                         }
21051                     }
21052 
21053                     if(!drawmethod->table)
21054                     {
21055 
21056                         if(drawmethod->remap >= 1 && drawmethod->remap <= e->modeldata.maps_loaded)
21057                         {
21058                             drawmethod->table = model_get_colourmap(&(e->modeldata), drawmethod->remap);
21059                         }
21060 
21061                         if(e->colourmap)
21062                         {
21063                             if(drawmethod->remap < 0)
21064                             {
21065                                 drawmethod->table = e->colourmap;
21066                             }
21067                         }
21068                         if(!drawmethod->table)
21069                         {
21070                             drawmethod->table = e->modeldata.palette;
21071                         }
21072                         if(e->modeldata.globalmap)
21073                         {
21074                             if(level && current_palette)
21075                             {
21076                                 drawmethod->table = level->palettes[current_palette - 1];
21077                             }
21078                             else
21079                             {
21080                                 drawmethod->table = pal;
21081                             }
21082                         }
21083                     }
21084                     if(e->dying)    // Code for doing dying flash
21085                     {
21086                         if((e->health <= e->per1 && e->health > e->per2 && (time % (GAME_SPEED / 5)) < (GAME_SPEED / 10)) ||
21087                                 (e->health <= e->per2 && (time % (GAME_SPEED / 10)) < (GAME_SPEED / 20)))
21088                         {
21089                             if(e->health > 0 )
21090                             {
21091                                 if(e->dying2 > 0)
21092                                 {
21093                                     if(e->health <= e->per1 && e->health > e->per2)
21094                                     {
21095                                         drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying);
21096                                     }
21097                                     else if(e->health <= e->per2)
21098                                     {
21099                                         drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying2);
21100                                     }
21101                                 }
21102                                 else drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying);
21103                             }
21104                         }
21105                     }
21106 
21107                     if(e->direction == DIRECTION_LEFT)
21108                     {
21109                         drawmethod->flipx = !drawmethod->flipx;
21110                         if(drawmethod->fliprotate && drawmethod->rotate)
21111                         {
21112                             drawmethod->rotate = 360 - drawmethod->rotate;
21113                         }
21114                     }
21115 
21116                     if(!use_mirror || z > MIRROR_Z) // don't display if behind the mirror
21117                     {
21118                         //just a simple check, doesn't work with mirror nor gfxshadow
21119                         if(drawmethod->clipw)
21120                         {
21121                             drawmethod->clipx += (int)(e->position.x - scrx);
21122                             drawmethod->clipy += (int)(e->position.z - e->position.y - scry);
21123                         }
21124                         spriteq_add_sprite((int)(e->position.x - scrx), (int)(e->position.z - e->position.y - scry), z, f, drawmethod, sortid);
21125                     }
21126 
21127                     can_mirror = (use_mirror && self->position.z > MIRROR_Z);
21128                     if(can_mirror)
21129                     {
21130                         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);
21131                     }
21132                 }//end of if(f<sprites_loaded)
21133 
21134                 if(e->modeldata.gfxshadow == 1 && f < sprites_loaded) //gfx shadow
21135                 {
21136                     useshadow = (e->animation->shadow ? e->animation->shadow[e->animpos] : 1) && shadowcolor && light.y;
21137                     //printf("\n %d, %d, %d\n", shadowcolor, light.x, light.y);
21138 
21139                     if(useshadow && e->position.y >= 0 && (!e->modeldata.aironly || (e->modeldata.aironly && inair(e))))
21140                     {
21141                         wall = checkwall_below(e->position.x, e->position.z, e->position.y);
21142                         if(wall < 0)
21143                         {
21144                             alty = (int)e->position.y;
21145                             temp1 = -1 * e->position.y * light.x / 256; // xshift
21146                             temp2 = (float)(-alty * light.y / 256);               // zshift
21147                             qx = (int)(e->position.x - scrx/* + temp1*/);
21148                             qy = (int)(e->position.z - scry/* +  temp2*/);
21149                         }
21150                         else
21151                         {
21152                             alty = (int)(e->position.y - level->walls[wall].height);
21153                             temp1 = -1 * (e->position.y - level->walls[wall].height) * light.x / 256; // xshift
21154                             temp2 = (float)(-alty * light.y / 256);               // zshift
21155                             qx = (int)(e->position.x - scrx/* + temp1*/);
21156                             qy = (int)(e->position.z - scry /*+  temp2*/ - level->walls[wall].height);
21157                         }
21158 
21159                         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
21160 
21161                         if(other && other != e && e->position.y >= other->position.y + other->animation->platform[other->animpos][7] && !(e->modeldata.shadowbase&1) )
21162                         {
21163                             alty = (int)(e->position.y - (other->position.y + other->animation->platform[other->animpos][7]));
21164                             temp1 = -1 * (e->position.y - (other->position.y + other->animation->platform[other->animpos][7])) * light.x / 256; // xshift
21165                             temp2 = (float)(-e->position.y * light.y / 256);
21166 
21167                             qx = (int)( e->position.x - scrx );
21168                             qy = (int)( e->position.z - scry - other->position.y - other->animation->platform[other->animpos][7] ); // + (other->animation->platform[other->animpos][6]/2)
21169                             //qy = (int)( e->position.z - e->position.y - scry + (e->position.y-e->base) );
21170                         }
21171 
21172                         if(basemap > 0 && !(e->modeldata.shadowbase&1))
21173                         {
21174                             alty = (int)(e->position.y - basemap);
21175                             temp1 = -1 * (e->position.y - basemap) * light.x / 256; // xshift
21176                             temp2 = (float)(-alty * light.y / 256);               // zshift
21177                             qx = (int)(e->position.x - scrx);
21178                             qy = (int)(e->position.z - scry - basemap);
21179                         }
21180 
21181                         //TODO check platforms, don't want to go through the entity list again right now // && !other after wall2
21182                         if(!(checkhole(e->position.x + temp1, e->position.z + temp2) && wall2 < 0 && !other) ) //&& !(wall>=0 && level->walls[wall].height>e->position.y))
21183                         {
21184                             if(wall >= 0 && wall2 >= 0)
21185                             {
21186                                 alty += (int)(level->walls[wall].height - level->walls[wall2].height);
21187                                 /*qx += -1*(level->walls[wall].height-level->walls[wall2].height)*light.x/256;
21188                                 qy += (level->walls[wall].height-level->walls[wall2].height) - (level->walls[wall].height-level->walls[wall2].height)*light.y/256;*/
21189                             }
21190                             else if(wall >= 0)
21191                             {
21192                                 alty += (int)(level->walls[wall].height);
21193                                 /*qx += -1*level->walls[wall].height*light.x/256;
21194                                 qy += level->walls[wall].height - level->walls[wall].height*light.y/256;*/
21195                             }
21196                             else if(wall2 >= 0)
21197                             {
21198                                 alty -= (int)(level->walls[wall2].height);
21199                                 /*qx -= -1*level->walls[wall2].height*light.x/256;
21200                                 qy -= level->walls[wall2].height - level->walls[wall2].height*light.y/256;*/
21201                             }
21202 
21203                             /*if (other)
21204                             {
21205                                 alty += (int)(other->position.y + other->animation->platform[other->animpos][7]);
21206                             }*/
21207 
21208                             // set 2D-LIKE shadow
21209                             if ( (e->modeldata.shadowbase&2) ) alty = temp1 = temp2 = 0;
21210 
21211                             sy = (2 * MIRROR_Z - qy) - 2 * scry;
21212 
21213                             if ( other && !(e->modeldata.shadowbase&1) ) z = other->position.z + 1;
21214                             else z = shadowz;
21215 
21216                             sz = PANEL_Z - HUD_Z;
21217 
21218                             if(e->animation->shadow_coords)
21219                             {
21220                                 if(e->direction == DIRECTION_RIGHT)
21221                                 {
21222                                     qx += e->animation->shadow_coords[e->animpos][0];
21223                                 }
21224                                 else
21225                                 {
21226                                     qx -= e->animation->shadow_coords[e->animpos][0];
21227                                 }
21228                                 qy += e->animation->shadow_coords[e->animpos][1];
21229                                 sy -= e->animation->shadow_coords[e->animpos][1];
21230                             }
21231                             shadowmethod = plainmethod;
21232                             shadowmethod.fillcolor = (shadowcolor > 0 ? shadowcolor : 0);
21233                             shadowmethod.alpha = shadowalpha;
21234                             shadowmethod.channelb = shadowmethod.channelg = shadowmethod.channelr = shadowopacity;
21235                             shadowmethod.table = drawmethod->table;
21236                             shadowmethod.scalex = drawmethod->scalex;
21237                             shadowmethod.flipx = drawmethod->flipx;
21238                             shadowmethod.scaley = light.y * drawmethod->scaley / 256;
21239                             shadowmethod.flipy = drawmethod->flipy;
21240                             shadowmethod.centery += alty;
21241                             if(shadowmethod.flipy)
21242                             {
21243                                 shadowmethod.centery = -shadowmethod.centery;
21244                             }
21245                             if(shadowmethod.scaley < 0)
21246                             {
21247                                 shadowmethod.scaley = -shadowmethod.scaley;
21248                                 shadowmethod.flipy = !shadowmethod.flipy;
21249                             }
21250                             shadowmethod.rotate = drawmethod->rotate;
21251                             shadowmethod.shiftx = drawmethod->shiftx + light.x;
21252 
21253                             spriteq_add_sprite(qx, qy, z, f, &shadowmethod, 0);
21254                             if(use_mirror)
21255                             {
21256                                 shadowmethod.flipy = !shadowmethod.flipy;
21257                                 shadowmethod.centery = -shadowmethod.centery;
21258                                 spriteq_add_sprite(qx, sy, sz, f, &shadowmethod, 0);
21259                             }
21260                         }
21261                     }//end of gfxshadow
21262                 }
21263                 else //plain shadow
21264                 {
21265                     useshadow = e->animation->shadow ? e->animation->shadow[e->animpos] : e->modeldata.shadow;
21266                     if(useshadow < 0)
21267                     {
21268                         useshadow = e->modeldata.shadow;
21269                     }
21270                     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))))
21271                     {
21272                         if(other && other != e && e->position.y >= other->position.y + other->animation->platform[other->animpos][7] && !(e->modeldata.shadowbase&1))
21273                         {
21274                             qx = (int)(e->position.x - scrx);
21275                             qy =                 (int)(e->position.z  - other->position.y - other->animation->platform[other->animpos][7] - scry);
21276                             sy = (int)((2 * MIRROR_Z - e->position.z) - other->position.y - other->animation->platform[other->animpos][7] - scry);
21277 
21278                             z = (int)(other->position.z + 1);
21279                             sz = 2 * PANEL_Z - z;
21280                         }
21281                         else if(level && wall >= 0)// && e->position.y >= level->walls[wall].height)
21282                         {
21283                             qx = (int)(e->position.x - scrx);
21284                             qy = (int)(e->position.z - level->walls[wall].height - scry);
21285                             sy = (int)((2 * MIRROR_Z - e->position.z) - level->walls[wall].height - scry);
21286                             z = shadowz;
21287                             sz = PANEL_Z - HUD_Z;
21288                         }
21289                         else if(level && basemap > 0 && !(e->modeldata.shadowbase&1))
21290                         {
21291                             qx = (int)(e->position.x - scrx);
21292                             qy = (int)(e->position.z - basemap - scry);
21293                             sy = (int)((2 * MIRROR_Z - e->position.z) - basemap - scry);
21294                             z = shadowz;
21295                             sz = PANEL_Z - HUD_Z;
21296                         }
21297                         else
21298                         {
21299                             qx = (int)(e->position.x - scrx);
21300                             qy = (int)(e->position.z - scry);
21301                             sy = (int)((2 * MIRROR_Z - e->position.z) - scry);
21302                             z = shadowz;
21303                             sz = PANEL_Z - HUD_Z;
21304                         }
21305                         if(e->animation->shadow_coords)
21306                         {
21307                             if(e->direction == DIRECTION_RIGHT)
21308                             {
21309                                 qx += e->animation->shadow_coords[e->animpos][0];
21310                             }
21311                             else
21312                             {
21313                                 qx -= e->animation->shadow_coords[e->animpos][0];
21314                             }
21315                             qy += e->animation->shadow_coords[e->animpos][1];
21316                             sy -= e->animation->shadow_coords[e->animpos][1];
21317                         }
21318 
21319                         shadowmethod = plainmethod;
21320                         shadowmethod.alpha = BLEND_MULTIPLY + 1;
21321                         shadowmethod.flipx = !e->direction;
21322 
21323                         spriteq_add_sprite(qx, qy, z, shadowsprites[useshadow - 1], &shadowmethod, 0);
21324                         if(use_mirror)
21325                         {
21326                             spriteq_add_sprite(qx, sy, sz, shadowsprites[useshadow - 1], &shadowmethod, 0);
21327                         }
21328                     }//end of plan shadow
21329                 }
21330             }// end of blink checking
21331 
21332             if(e->arrowon)    // Display the players image while invincible to indicate player number
21333             {
21334                 if(e->modeldata.parrow[(int)e->playerindex][0] && e->invincible == 1)
21335                 {
21336                     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);
21337                 }
21338             }
21339         }// end of if(ent_list[i]->exists)
21340 
21341         // reset vars for next loop
21342 
21343     }// end of for
21344 
21345     //defer ondraw script so it can manage spriteq better
21346     for(i = 0; i < ent_stack_size; i++)
21347     {
21348         execute_ondraw_script(ent_stack[i]);
21349     }
21350     ent_stack_size = 0;
21351 }
21352 
21353 
21354 
toss(entity * ent,float lift)21355 void toss(entity *ent, float lift)
21356 {
21357     if(!lift)
21358     {
21359         return;    //zero?
21360     }
21361     ent->toss_time = time + 1;
21362     ent->velocity.y = lift;
21363     ent->position.y += 0.5;        // Get some altitude (needed for checks)
21364 }
21365 
21366 
21367 
findent(int types)21368 entity *findent(int types)
21369 {
21370     int i;
21371     for(i = 0; i < ent_max; i++)
21372     {
21373         // 2007-12-18, remove all nodieblink checking, because dead corpse with nodieblink 3 will be changed to TYPE_NONE
21374         // so if it is "dead" and TYPE_NONE, it must be a corpse
21375         if(ent_list[i]->exists && (ent_list[i]->modeldata.type & types) && !(ent_list[i]->dead && ent_list[i]->modeldata.type == TYPE_NONE))
21376         {
21377             return ent_list[i];
21378         }
21379     }
21380     return NULL;
21381 }
21382 
21383 
21384 
count_ents(int types)21385 int count_ents(int types)
21386 {
21387     int i;
21388     int count = 0;
21389     for(i = 0; i < ent_max; i++)
21390     {
21391         // 2007-12-18, remove all nodieblink checking, because dead corpse with nodieblink 3 will be changed to TYPE_NONE
21392         // so if it is "dead" and TYPE_NONE, it must be a corpse
21393         count += (ent_list[i]->exists && (ent_list[i]->modeldata.type & types) );
21394     }
21395     return count;
21396 }
21397 
isItem(entity * e)21398 int isItem(entity *e)
21399 {
21400     return e->modeldata.type & TYPE_ITEM;
21401 }
21402 
isSubtypeTouch(entity * e)21403 int isSubtypeTouch(entity *e)
21404 {
21405     return e->modeldata.subtype == SUBTYPE_TOUCH;
21406 }
21407 
isSubtypeWeapon(entity * e)21408 int isSubtypeWeapon(entity *e)
21409 {
21410     return e->modeldata.subtype == SUBTYPE_WEAPON;
21411 }
21412 
isSubtypeProjectile(entity * e)21413 int isSubtypeProjectile(entity *e)
21414 {
21415     return e->modeldata.subtype == SUBTYPE_PROJECTILE;
21416 }
21417 
canBeDamaged(entity * who,entity * bywhom)21418 int canBeDamaged(entity *who, entity *bywhom)
21419 {
21420     return (who->modeldata.candamage & bywhom->modeldata.type) == bywhom->modeldata.type;
21421 }
21422 
21423 //check if an item is usable by the entity
normal_test_item(entity * ent,entity * item)21424 int normal_test_item(entity *ent, entity *item)
21425 {
21426     return (
21427                isItem(item) &&
21428                (item->modeldata.stealth.hide <= ent->modeldata.stealth.detect) &&
21429                diff(item->position.x, ent->position.x) + diff(item->position.z, ent->position.z) < videomodes.hRes / 2 &&
21430                item->animation->vulnerable[item->animpos] && !item->blink &&
21431                (validanim(ent, ANI_GET) || (isSubtypeTouch(item) && canBeDamaged(item, ent))) &&
21432                (
21433                    (isSubtypeWeapon(item) && !ent->weapent && ent->modeldata.weapon &&
21434                     ent->modeldata.numweapons >= item->modeldata.weapnum && ent->modeldata.weapon[item->modeldata.weapnum - 1] >= 0)
21435                    || (isSubtypeProjectile(item) && !ent->weapent)
21436                    || (item->health && (ent->health < ent->modeldata.health) && ! isSubtypeProjectile(item) && ! isSubtypeWeapon(item))
21437                )
21438            );
21439 }
21440 
test_item(entity * ent,entity * item)21441 int test_item(entity *ent, entity *item)
21442 {
21443     if (!(
21444                 isItem(item) &&
21445                 item->animation->vulnerable[item->animpos] && !item->blink &&
21446                 (validanim(ent, ANI_GET) || (isSubtypeTouch(item) && canBeDamaged(item, ent)))
21447             ))
21448     {
21449         return 0;
21450     }
21451     if(isSubtypeProjectile(item) && ent->weapent)
21452     {
21453         return 0;
21454     }
21455     if(isSubtypeWeapon(item) &&
21456             (ent->weapent || !ent->modeldata.weapon ||
21457              ent->modeldata.numweapons < item->modeldata.weapnum ||
21458              ent->modeldata.weapon[item->modeldata.weapnum - 1] < 0)
21459       )
21460     {
21461         return 0;
21462     }
21463     return 1;
21464 }
21465 
player_test_pickable(entity * ent,entity * item)21466 int player_test_pickable(entity *ent, entity *item)
21467 {
21468     if(isSubtypeTouch(item))
21469     {
21470         return 0;
21471     }
21472     if(isSubtypeWeapon(item) && ent->modeldata.animal == 2)
21473     {
21474         return 0;
21475     }
21476     if(diff(ent->base , item->position.y) > 0.1)
21477     {
21478         return 0;
21479     }
21480     return test_item(ent, item);
21481 }
21482 
player_test_touch(entity * ent,entity * item)21483 int player_test_touch(entity *ent, entity *item)
21484 {
21485     if(!isSubtypeTouch(item))
21486     {
21487         return 0;
21488     }
21489     if(isSubtypeWeapon(item) && ent->modeldata.animal == 2)
21490     {
21491         return 0;
21492     }
21493     if(diff(ent->base , item->position.y) > 1)
21494     {
21495         return 0;
21496     }
21497     return test_item(ent, item);
21498 }
21499 
find_ent_here(entity * exclude,float x,float z,int types,int (* test)(entity *,entity *))21500 entity *find_ent_here(entity *exclude, float x, float z, int types, int (*test)(entity *, entity *))
21501 {
21502     int i;
21503     for(i = 0; i < ent_max; i++)
21504     {
21505         if( ent_list[i]->exists
21506                 && ent_list[i] != exclude
21507                 && (ent_list[i]->modeldata.type & types)
21508                 && diff(ent_list[i]->position.x, x) < (self->modeldata.grabdistance * 0.83333)
21509                 && diff(ent_list[i]->position.z, z) < (self->modeldata.grabdistance / 3)
21510                 && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
21511                 && (!test || test(exclude, ent_list[i]))
21512           )
21513         {
21514             return ent_list[i];
21515         }
21516     }
21517     return NULL;
21518 }
21519 
set_idle(entity * ent)21520 int set_idle(entity *ent)
21521 {
21522     //int ani = ANI_IDLE;
21523     //if(validanim(ent,ANI_FAINT) && ent->health <= ent->modeldata.health / 4) ani = ANI_FAINT;
21524     //if(validanim(ent,ani)) ent_set_anim(ent, ani, 0);
21525     ent->idling = 1;
21526     ent->attacking = 0;
21527     ent->inpain = 0;
21528     ent->inbackpain = 0;
21529     ent->falling = 0;
21530     ent->jumping = 0;
21531     ent->blocking = 0;
21532     common_idle_anim(ent);
21533     return 1;
21534 }
21535 
set_death(entity * iDie,int type,int reset)21536 int set_death(entity *iDie, int type, int reset)
21537 {
21538     int die = 0;
21539 
21540     //iDie->velocity.x = iDie->velocity.z = iDie->velocity.y = 0; // stop the target
21541     if(iDie->blocking && validanim(iDie, ANI_CHIPDEATH))
21542     {
21543         ent_set_anim(iDie, ANI_CHIPDEATH, reset);
21544         iDie->idling = 0;
21545         iDie->getting = 0;
21546         iDie->jumping = 0;
21547         iDie->charging = 0;
21548         iDie->attacking = 0;
21549         iDie->blocking = 0;
21550         return 1;
21551     }
21552 
21553     if(type < 0 || type >= max_attack_types)
21554     {
21555         type = 0;
21556     }
21557 
21558     if ( iDie->inbackpain ) die = animbackdies[type];
21559     else die = animdies[type];
21560 
21561     if(validanim(iDie, die))
21562     {
21563         ent_set_anim(iDie, die, reset);
21564     }
21565     else if( iDie->inbackpain && validanim(iDie, animbackdies[0]) )
21566     {
21567         ent_set_anim(iDie, animbackdies[0], reset);
21568     }
21569     else if(validanim(iDie, animdies[type]))
21570     {
21571         if ( iDie->inbackpain ) reset_backpain(iDie);
21572         iDie->inbackpain = 0;
21573         ent_set_anim(iDie, animdies[type], reset);
21574     }
21575     else if(validanim(iDie, animdies[0]))
21576     {
21577         if ( iDie->inbackpain ) reset_backpain(iDie);
21578         iDie->inbackpain = 0;
21579         ent_set_anim(iDie, animdies[0], reset);
21580     }
21581     else
21582     {
21583         return 0;
21584     }
21585 
21586     iDie->idling = 0;
21587     iDie->getting = 0;
21588     iDie->jumping = 0;
21589     iDie->charging = 0;
21590     iDie->attacking = 0;
21591     iDie->blocking = 0;
21592     if(iDie->frozen)
21593     {
21594         unfrozen(iDie);
21595     }
21596     return 1;
21597 }
21598 
21599 
set_fall(entity * iFall,int type,int reset,entity * other,int force,int drop,int noblock,int guardcost,int jugglecost,int pauseadd,int tag)21600 int set_fall(entity *iFall, int type, int reset, entity *other, int force, int drop, int noblock, int guardcost, int jugglecost, int pauseadd, int tag)
21601 {
21602     int fall = 0;
21603 
21604     if( type < 0 || type >= max_attack_types )
21605     {
21606         type = 0;
21607     }
21608 
21609     if ( iFall->inbackpain ) fall = animbackfalls[type];
21610     else fall = animfalls[type];
21611 
21612     if(validanim(iFall, fall))
21613     {
21614         ent_set_anim(iFall, fall, reset);
21615     }
21616     else if( iFall->inbackpain && validanim(iFall, animbackfalls[0]) )
21617     {
21618         ent_set_anim(iFall, animbackfalls[0], reset);
21619     }
21620     else if( validanim(iFall, animfalls[type]) )
21621     {
21622         if ( iFall->inbackpain ) reset_backpain(iFall);
21623         iFall->inbackpain = 0;
21624         ent_set_anim(iFall, animfalls[type], reset);
21625     }
21626     else if(validanim(iFall, animfalls[0]))
21627     {
21628         if ( iFall->inbackpain ) reset_backpain(iFall);
21629         iFall->inbackpain = 0;
21630         ent_set_anim(iFall, animfalls[0], reset);
21631     }
21632     else
21633     {
21634         return 0;
21635     }
21636 
21637     iFall->drop = 1;
21638     iFall->inpain = 0;
21639     iFall->idling = 0;
21640     iFall->falling = 1;
21641     iFall->jumping = 0;
21642     iFall->getting = 0;
21643     iFall->charging = 0;
21644     iFall->attacking = 0;
21645     iFall->blocking = 0;
21646     iFall->nograb = 1;
21647 
21648     if(iFall->frozen)
21649     {
21650         unfrozen(iFall);
21651     }
21652     execute_onfall_script(iFall, other, force, drop, type, noblock, guardcost, jugglecost, pauseadd, tag);
21653 
21654     return 1;
21655 }
21656 
set_rise(entity * iRise,int type,int reset)21657 int set_rise(entity *iRise, int type, int reset)
21658 {
21659     if(type < 0 || type >= max_attack_types || !validanim(iRise, animrises[type]))
21660     {
21661         type = 0;
21662     }
21663     if(validanim(iRise, animrises[type]))
21664     {
21665         ent_set_anim(iRise, animrises[type], reset);
21666     }
21667     else
21668     {
21669         return 0;
21670     }
21671     iRise->takeaction = common_rise;
21672     // Get up again
21673     iRise->drop = 0;
21674     iRise->falling = 0;
21675     iRise->inbackpain = 0;
21676     iRise->projectile = 0;
21677     iRise->nograb = iRise->nograb_default; //iRise->nograb = 0;
21678     iRise->velocity.x = self->velocity.z = self->velocity.y = 0;
21679     iRise->modeldata.jugglepoints.current = iRise->modeldata.jugglepoints.max; //reset jugglepoints
21680     return 1;
21681 }
21682 
set_riseattack(entity * iRiseattack,int type,int reset)21683 int set_riseattack(entity *iRiseattack, int type, int reset)
21684 {
21685     if(!validanim(iRiseattack, animriseattacks[type]) && iRiseattack->modeldata.riseattacktype == 1)
21686     {
21687         type = 0;
21688     }
21689     if(iRiseattack->modeldata.riseattacktype == 0 || type < 0 || type >= max_attack_types)
21690     {
21691         type = 0;
21692     }
21693     if(!validanim(iRiseattack, animriseattacks[type]))
21694     {
21695         return 0;
21696     }
21697 
21698     iRiseattack->takeaction = common_attack_proc;
21699     self->staydown.riseattack_stall = 0;			//Reset riseattack delay.
21700     set_attacking(iRiseattack);
21701     iRiseattack->drop = 0;
21702     iRiseattack->nograb = iRiseattack->nograb_default; //iRiseattack->nograb = 0;
21703     iRiseattack->modeldata.jugglepoints.current = iRiseattack->modeldata.jugglepoints.max; //reset jugglepoints
21704     ent_set_anim(iRiseattack, animriseattacks[type], 0);
21705     return 1;
21706 }
21707 
21708 #define painflags(iPain) \
21709 	iPain->idling = 0;\
21710 	iPain->falling = 0;\
21711 	iPain->projectile = 0;\
21712 	iPain->drop = 0;\
21713 	iPain->attacking = 0;\
21714 	iPain->getting = 0;\
21715 	iPain->charging = 0;\
21716 	iPain->jumping = 0;\
21717 	iPain->blocking = 0;\
21718 	iPain->inpain = 1;\
21719 	if(iPain->frozen) unfrozen(iPain);
21720 
set_blockpain(entity * iBlkpain,int type,int reset)21721 int set_blockpain(entity *iBlkpain, int type, int reset)
21722 {
21723     if(type < 0 || type >= max_attack_types || !validanim(iBlkpain, animblkpains[type]))
21724     {
21725         type = 0;
21726     }
21727     if(validanim(iBlkpain, animblkpains[type]))
21728     {
21729         iBlkpain->takeaction = common_block;
21730         set_blocking(self);
21731         iBlkpain->inpain = 1;
21732         ent_set_anim(iBlkpain, animblkpains[type], reset);
21733         return 1;
21734     }
21735     return 0;
21736 }
21737 
reset_backpain(entity * ent)21738 int reset_backpain(entity *ent)
21739 {
21740     if (ent->normaldamageflipdir >= 0)
21741     {
21742         if (ent->normaldamageflipdir == DIRECTION_RIGHT) ent->direction = DIRECTION_RIGHT;
21743         else ent->direction = DIRECTION_LEFT;
21744 
21745         if(ent->direction == DIRECTION_RIGHT) ent->velocity.x = -1*ABS(ent->velocity.x);
21746         else ent->velocity.x = ABS(ent->velocity.x);
21747 
21748         return 1;
21749     }
21750 
21751     return 0;
21752 }
21753 
check_backpain(entity * attacker,entity * defender)21754 int check_backpain(entity* attacker, entity* defender) {
21755     if ( !defender->modeldata.backpain ) return 0;
21756     if ( defender->inpain ) return 0;
21757     if ( defender->falling ) return 0;
21758     if ( defender->dead ) return 0;
21759     if ( ((!defender->direction && attacker->position.x > defender->position.x) || (defender->direction && attacker->position.x < defender->position.x)) )
21760     {
21761         defender->inbackpain = 1;
21762         return 1;
21763     } else if ( defender->inbackpain ) defender->inbackpain = 0;
21764 
21765     return 0;
21766 }
21767 
set_pain(entity * iPain,int type,int reset)21768 int set_pain(entity *iPain, int type, int reset)
21769 {
21770     int pain = 0;
21771 
21772     iPain->velocity.x = iPain->velocity.z = iPain->velocity.y = 0; // stop the target
21773     if(iPain->modeldata.guardpoints.max > 0 && iPain->modeldata.guardpoints.current <= 0)
21774     {
21775         pain = ANI_GUARDBREAK;
21776         iPain->modeldata.guardpoints.current = iPain->modeldata.guardpoints.max;
21777     }
21778     else if(type == -1 || type >= max_attack_types)
21779     {
21780         pain = ANI_GRABBED;
21781     }
21782     else
21783     {
21784         if ( iPain->inbackpain ) pain = animbackpains[type];
21785         else pain = animpains[type];
21786     }
21787 
21788 
21789     if(validanim(iPain, pain))
21790     {
21791         ent_set_anim(iPain, pain, reset);
21792     }
21793     else if( iPain->inbackpain && validanim(iPain, animbackpains[0]) )
21794     {
21795         ent_set_anim(iPain, animbackpains[0], reset);
21796     }
21797     else if( (type != -1 && type < max_attack_types) && validanim(iPain, animpains[type]) )
21798     {
21799         if ( iPain->inbackpain ) reset_backpain(iPain);
21800         iPain->inbackpain = 0;
21801         ent_set_anim(iPain, animpains[type], reset);
21802     }
21803     else if(validanim(iPain, animpains[0]))
21804     {
21805         if ( iPain->inbackpain ) reset_backpain(iPain);
21806         iPain->inbackpain = 0;
21807         ent_set_anim(iPain, animpains[0], reset);
21808     }
21809     else if(validanim(iPain, ANI_IDLE))
21810     {
21811         if ( iPain->inbackpain ) reset_backpain(iPain);
21812         iPain->inbackpain = 0;
21813         ent_set_anim(iPain, ANI_IDLE, reset);
21814     }
21815     else
21816     {
21817         return 0;
21818     }
21819 
21820     painflags(iPain);
21821 
21822     if(pain == ANI_GRABBED)
21823     {
21824         iPain->inpain = 0;
21825         if ( iPain->inbackpain ) reset_backpain(iPain);
21826         iPain->inbackpain = 0;
21827     }
21828 
21829     execute_onpain_script(iPain, type, reset);
21830     return 1;
21831 }
21832 
21833 
21834 //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)21835 void set_model_ex(entity *ent, char *modelname, int index, s_model *newmodel, int anim_flag)
21836 {
21837     s_model *model = NULL;
21838     entity tempe;
21839     s_defense *dfs = NULL;
21840     float *ofs = NULL;
21841     int   i;
21842     int   type = ent->modeldata.type;
21843 
21844     model = ent->model;
21845     tempe.exists = 0;
21846 
21847     if(!newmodel)
21848     {
21849         if(index >= 0)
21850         {
21851             newmodel = model_cache[index].model;
21852         }
21853         else
21854         {
21855             newmodel = findmodel(modelname);
21856         }
21857     }
21858     if(!newmodel)
21859     {
21860         shutdown(1, "Can't set model for entity '%s', model not found.\n", ent->name);
21861     }
21862     if(newmodel == model)
21863     {
21864         return;
21865     }
21866 
21867     if(!(newmodel->model_flag & MODEL_NO_COPY))
21868     {
21869         if(!newmodel->speed)
21870         {
21871             newmodel->speed = model->speed;
21872         }
21873         if(!newmodel->runspeed)
21874         {
21875             newmodel->runspeed = model->runspeed;
21876             newmodel->runjumpheight = model->runjumpheight;
21877             newmodel->runjumpdist = model->runjumpdist;
21878             newmodel->runupdown = model->runupdown;
21879             newmodel->runhold = model->runhold;
21880         }
21881         if(newmodel->icon.def           <   0)
21882         {
21883             newmodel->icon.def          = model->icon.def;
21884         }
21885         if(newmodel->icon.pain       <   0)
21886         {
21887             newmodel->icon.pain      = model->icon.pain;
21888         }
21889         if(newmodel->icon.get        <   0)
21890         {
21891             newmodel->icon.get       = model->icon.get;
21892         }
21893         if(newmodel->icon.die        <   0)
21894         {
21895             newmodel->icon.die       = model->icon.die;
21896         }
21897         if(newmodel->shadow         <   0)
21898         {
21899             newmodel->shadow        = model->shadow;
21900         }
21901         if(newmodel->knife          <   0)
21902         {
21903             newmodel->knife         = model->knife;
21904         }
21905         if(newmodel->pshotno        <   0)
21906         {
21907             newmodel->pshotno       = model->pshotno;
21908         }
21909         if(newmodel->bomb           <   0)
21910         {
21911             newmodel->bomb          = model->bomb;
21912         }
21913         if(newmodel->star           <   0)
21914         {
21915             newmodel->star          = model->star;
21916         }
21917         if(newmodel->flash          <   0)
21918         {
21919             newmodel->flash         = model->flash;
21920         }
21921         if(newmodel->bflash         <   0)
21922         {
21923             newmodel->bflash        = model->bflash;
21924         }
21925         if(newmodel->dust.fall_land        <   0)
21926         {
21927             newmodel->dust.fall_land       = model->dust.fall_land;
21928         }
21929         if(newmodel->dust.jump_land  <   0)
21930         {
21931             newmodel->dust.jump_land       = model->dust.jump_land;
21932         }
21933         if(newmodel->diesound       <   0)
21934         {
21935             newmodel->diesound      = model->diesound;
21936         }
21937 
21938         for(i = 0; i < max_animations; i++)
21939         {
21940             if(!newmodel->animation[i] && model->animation[i] && model->animation[i]->numframes > 0)
21941             {
21942                 newmodel->animation[i] = model->animation[i];
21943             }
21944         }
21945         // copy the weapon list if model flag is not set to use its own weapon list
21946         if(!(newmodel->model_flag & MODEL_NO_WEAPON_COPY))
21947         {
21948             newmodel->weapnum = model->weapnum;
21949             if(!newmodel->weapon)
21950             {
21951                 newmodel->weapon = model->weapon;
21952                 newmodel->numweapons = model->numweapons;
21953             }
21954         }
21955     }
21956 
21957     //Make a shallow copy of old entity values, not safe but easy.
21958     //Also copy offense and defense because they are more likely be used by weapons,
21959     //other references are left alone for now
21960     if(Script_IsInitialized(newmodel->scripts->onmodelcopy_script))
21961     {
21962         tempe = *ent;
21963         dfs = malloc(sizeof(*dfs) * max_attack_types);
21964         ofs = malloc(sizeof(*ofs) * max_attack_types);
21965         memcpy(dfs, ent->defense, sizeof(*dfs)*max_attack_types);
21966         memcpy(ofs, ent->offense_factors, sizeof(*ofs)*max_attack_types);
21967         tempe.defense = dfs;
21968         tempe.offense_factors = ofs;
21969     }
21970 
21971     ent_set_model(ent, newmodel->name, anim_flag);
21972 
21973     ent->modeldata.type = type;
21974 
21975     if((newmodel->model_flag & MODEL_NO_SCRIPT_COPY))
21976     {
21977         clear_all_scripts(ent->scripts, 0);
21978     }
21979 
21980     copy_all_scripts(newmodel->scripts, ent->scripts, 0);
21981     memcpy(ent->defense, ent->modeldata.defense, sizeof(*ent->defense)*max_attack_types);
21982     memcpy(ent->offense_factors, ent->modeldata.offense_factors, sizeof(*ent->offense_factors)*max_attack_types);
21983 
21984     ent_set_colourmap(ent, ent->map);
21985     if(Script_IsInitialized(ent->scripts->onmodelcopy_script))
21986     {
21987         execute_onmodelcopy_script(ent, &tempe);
21988         if(ofs)
21989         {
21990             free(ofs);
21991         }
21992         if(dfs)
21993         {
21994             free(dfs);
21995         }
21996     }
21997 }
21998 
set_weapon(entity * ent,int wpnum,int anim_flag)21999 void set_weapon(entity *ent, int wpnum, int anim_flag) // anim_flag added for scripted midair weapon changing
22000 {
22001     if(!ent)
22002     {
22003         return;
22004     }
22005 //printf("setweapon: %d \n", wpnum);
22006 
22007     if(ent->modeldata.type & TYPE_PLAYER) // save current weapon for player's weaploss 3
22008     {
22009         if(ent->modeldata.weaploss[0] == WEAPLOSS_TYPE_CHANGE)
22010         {
22011             player[(int)ent->playerindex].weapnum = wpnum;
22012         }
22013         else
22014         {
22015             player[(int)ent->playerindex].weapnum = level->setweap;
22016         }
22017     }
22018 
22019     if(ent->modeldata.weapon && wpnum > 0 && wpnum <= ent->modeldata.numweapons && ent->modeldata.weapon[wpnum - 1])
22020     {
22021         set_model_ex(ent, NULL, ent->modeldata.weapon[wpnum - 1], NULL, !anim_flag);
22022     }
22023     else
22024     {
22025         set_model_ex(ent, NULL, -1, ent->defaultmodel, 1);
22026     }
22027 }
22028 
22029 //////////////////////////////////////////////////////////////////////////
22030 //                  common A.I. code for enemies & NPCs
22031 //////////////////////////////////////////////////////////////////////////
22032 
22033 
melee_find_target()22034 entity *melee_find_target()
22035 {
22036     return NULL;
22037 }
22038 
long_find_target()22039 entity *long_find_target()
22040 {
22041     return NULL;
22042 }
22043 
block_find_target(int anim,int detect_adj)22044 entity *block_find_target(int anim, int detect_adj)
22045 {
22046     int i , min, max, instance, detect;
22047     int index = -1;
22048     min = 0;
22049     max = 9999;
22050     float diffx, diffz, diffd, diffo = 0;
22051     entity      *attacker;
22052 
22053     detect = detect_adj + self->modeldata.stealth.detect;
22054 
22055     //find the 'nearest' attacking one
22056     for(i = 0; i < ent_max; i++)
22057     {
22058         attacker = ent_list[i];
22059 
22060         for(instance = 0; instance < max_collisons; instance++)
22061         {
22062             if( attacker && attacker->exists && attacker != self //cant target self
22063                 && (attacker->modeldata.candamage & self->modeldata.type)
22064                 && (anim < 0 || (anim >= 0 && check_range(self, attacker, anim)))
22065                 && !attacker->dead && attacker->attacking//must be alive
22066                 && attacker->animation->collision_attack && attacker->animation->collision_attack[attacker->animpos] && attacker->animation->collision_attack[attacker->animpos]->instance
22067                 && ( !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) )
22068                 && (diffd = (diffx = diff(attacker->position.x, self->position.x)) + (diffz = diff(attacker->position.z, self->position.z))) >= min
22069                 && diffd <= max
22070                 && (attacker->modeldata.stealth.hide <= detect) //Stealth factor less then perception factor (allows invisibility).
22071               )
22072             {
22073                 if(index < 0 || diffd < diffo)
22074                 {
22075                     index = i;
22076                     diffo = diffd;
22077 
22078                     continue;
22079                 }
22080             }
22081         }
22082     }
22083     if( index >= 0)
22084     {
22085         return ent_list[index];
22086     }
22087     return NULL;
22088 }
22089 
normal_find_target(int anim,int detect_adj)22090 entity *normal_find_target(int anim, int detect_adj)
22091 {
22092 
22093     /*
22094     normal_find_target
22095     Author unknown
22096     Date unknown
22097     ~Damon Caskey, 2011_07_22: Add support for detect adjustment.
22098 
22099     int anim:           Animation find range will be calculated by. Default to current animation if not passed.
22100     int detect_adj:     Local detection adjustment. Allows lesser or greater penetration of target's stealth for location.
22101     */
22102 
22103     int i , min, max, detect;
22104     int index = -1;
22105     min = 0;
22106     max = 9999;
22107     float diffx, diffz, diffd, diffo = 0;
22108 
22109     detect = detect_adj + self->modeldata.stealth.detect;
22110 
22111     //find the 'nearest' one
22112     for(i = 0; i < ent_max; i++)
22113     {
22114         if( ent_list[i]->exists && ent_list[i] != self //cant target self
22115                 && (ent_list[i]->modeldata.type & self->modeldata.hostile)
22116                 && (anim < 0 || (anim >= 0 && check_range(self, ent_list[i], anim)))
22117                 && !ent_list[i]->dead //must be alive
22118                 && (diffd = (diffx = diff(ent_list[i]->position.x, self->position.x)) + (diffz = diff(ent_list[i]->position.z, self->position.z))) >= min
22119                 && diffd <= max
22120                 && (ent_list[i]->modeldata.stealth.hide <= detect) //Stealth factor less then perception factor (allows invisibility).
22121           )
22122         {
22123 
22124             if(index < 0 || (index >= 0 && (!ent_list[index]->animation->vulnerable[ent_list[index]->animpos] || ent_list[index]->invincible == 1)) ||
22125                     (
22126                         (self->position.x < ent_list[i]->position.x) == (self->direction == DIRECTION_RIGHT) && // don't turn to the one on the back
22127                         //ent_list[i]->x >= advancex-10 && ent_list[i]->x<advancex+videomodes.hRes+10 && // don't turn to an offscreen target
22128                         //ent_list[i]->z >= advancey-10 && ent_list[i]->z<advancey+videomodes.vRes+10 &&
22129                         diffd < diffo
22130                     )
22131               )
22132             {
22133                 index = i;
22134                 diffo = diffd;
22135             }
22136         }
22137     }
22138     if( index >= 0)
22139     {
22140         return ent_list[index];
22141     }
22142     return NULL;
22143 }
22144 
22145 //Used by default A.I. pattern
22146 // A.I. characters try to find a pickable item
normal_find_item()22147 entity *normal_find_item()
22148 {
22149 
22150     int i;
22151     int index = -1;
22152     entity *ce = NULL;
22153     //find the 'nearest' one
22154     for(i = 0; i < ent_max; i++)
22155     {
22156         ce = ent_list[i];
22157 
22158         if( ce->exists && normal_test_item(self, ce) )
22159         {
22160             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))
22161             {
22162                 index = i;
22163             }
22164         }
22165     }
22166     if( index >= 0)
22167     {
22168         return ent_list[index];
22169     }
22170     return NULL;
22171 }
22172 
long_attack()22173 int long_attack()
22174 {
22175     return 0;
22176 }
22177 
melee_attack()22178 int melee_attack()
22179 {
22180     return 0;
22181 }
22182 
22183 // chose next attack in atchain, if succeeded, return 1, otherwise return 0.
perform_atchain()22184 int perform_atchain()
22185 {
22186     int pickanim = 0;
22187 
22188     if(self->modeldata.chainlength <= 0)
22189     {
22190         return 0;
22191     }
22192 
22193     if(self->combotime > time)
22194     {
22195         self->combostep[0]++;
22196     }
22197     else
22198     {
22199         self->combostep[0] = 1;
22200     }
22201 
22202     if(self->modeldata.atchain[self->combostep[0] - 1] == 0) // 0 means the chain ends
22203     {
22204         self->combostep[0] = 1;
22205     }
22206 
22207     if(validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) )
22208     {
22209         if(((self->combostep[0] == 1 || !(self->modeldata.combostyle & 1)) && (self->modeldata.type & TYPE_PLAYER)) || // player should use attack 1st step without checking range
22210 
22211                 (!(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)
22212 
22213                 ((self->modeldata.combostyle & 1) && normal_find_target(animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 0))) // combostyle 1 checks all anyway
22214         {
22215             pickanim = 1;
22216         }
22217         else if((self->modeldata.combostyle & 1) && self->combostep[0] != 1) // ranged combo? search for a valid attack
22218         {
22219 
22220             while(++self->combostep[0] <= self->modeldata.chainlength)
22221             {
22222                 if(self->modeldata.atchain[self->combostep[0] - 1] &&
22223                         validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) &&
22224                         (self->combostep[0] == self->modeldata.chainlength ||
22225                          normal_find_target(animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 0)))
22226                 {
22227                     pickanim = 1;
22228                     break;
22229                 }
22230             }
22231         }
22232     }
22233     else
22234     {
22235         self->combostep[0] = 0;
22236     }
22237     if(pickanim && validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]))
22238     {
22239         self->takeaction = common_attack_proc;
22240         set_attacking(self);
22241         ent_set_anim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 1);
22242     }
22243     if(!pickanim || self->combostep[0] > self->modeldata.chainlength)
22244     {
22245         self->combostep[0] = 0;
22246     }
22247     if((self->modeldata.combostyle & 2))
22248     {
22249         self->combotime = time + combodelay;
22250     }
22251     return pickanim;
22252 }
22253 
normal_prepare()22254 void normal_prepare()
22255 {
22256     int i, j;
22257     int found = 0, special = 0;
22258     int predir = self->direction;
22259 
22260     entity *target = normal_find_target(-1, 0);
22261 
22262     self->velocity.x = self->velocity.z = 0; //stop
22263 
22264     if(!target)
22265     {
22266         self->idling = 1;
22267         self->takeaction = NULL;
22268         return;
22269     }
22270 
22271     //check if target is behind, so we can perform a turn back animation
22272     if(!self->modeldata.noflip)
22273     {
22274         self->direction = (self->position.x < target->position.x);
22275     }
22276     if(predir != self->direction && validanim(self, ANI_TURN))
22277 
22278     {
22279         self->takeaction = common_turn;
22280         self->direction = predir;
22281         set_turning(self);
22282         ent_set_anim(self, ANI_TURN, 0);
22283         return;
22284     }
22285 
22286     // Wait...
22287     if(time < self->stalltime)
22288     {
22289         return;
22290     }
22291     // let go the projectile, well
22292     if( self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
22293             validanim(self, ANI_THROWATTACK) &&
22294             check_range(self, target, ANI_THROWATTACK))
22295     {
22296         self->takeaction = common_attack_proc;
22297         set_attacking(self);
22298         ent_set_anim(self, ANI_THROWATTACK, 0);
22299         return ;
22300     }
22301 
22302     // move freespecial check here
22303 
22304     for(i = 0; i < max_freespecials; i++)
22305     {
22306         if(validanim(self, animspecials[i]) &&
22307                 (check_energy(COST_CHECK_MP, animspecials[i]) ||
22308                  check_energy(COST_CHECK_HP, animspecials[i])) &&
22309                 check_range(self, target, animspecials[i]))
22310         {
22311             atkchoices[found++] = animspecials[i];
22312         }
22313     }
22314     if((rand32() & 7) < 2)
22315     {
22316         if(found && check_costmove(atkchoices[(rand32() & 0xffff) % found], 1, 0) )
22317         {
22318             return;
22319         }
22320     }
22321     special = found;
22322 
22323     if(self->modeldata.chainlength > 1) // have a chain?
22324     {
22325         if(perform_atchain())
22326         {
22327             return;
22328         }
22329     }
22330     else // dont have a chain so just select an attack randomly
22331     {
22332         // Pick an attack
22333         for(i = 0; i < max_attacks; i++)
22334         {
22335             if( validanim(self, animattacks[i]) &&
22336                     check_range(self, target, animattacks[i]))
22337             {
22338                 // a trick to make attack 1 has a greater chance to be chosen
22339                 // 6 5 4 3 2 1 1 1 1 1 ....
22340                 for(j = ((5 - i) >= 0 ? (5 - i) : 0); j >= 0; j--)
22341                 {
22342                     atkchoices[found++] = animattacks[i];
22343                 }
22344             }
22345         }
22346         if(found > special)
22347         {
22348             self->takeaction = common_attack_proc;
22349             set_attacking(self);
22350             ent_set_anim(self, atkchoices[special + (rand32() & 0xffff) % (found - special)], 0);
22351             return ;
22352         }
22353     }
22354 
22355     // if no attack was picked, just choose a random one from the valid list
22356     if(special && check_costmove(atkchoices[(rand32() & 0xffff) % special], 1, 0))
22357     {
22358         return;
22359     }
22360 
22361     // No attack to perform, return to A.I. root
22362     self->idling = 1;
22363     self->takeaction = NULL;
22364 }
22365 
common_jumpland()22366 void common_jumpland()
22367 {
22368     if(self->animating)
22369     {
22370         return;
22371     }
22372     self->takeaction = NULL;
22373     set_idle(self);
22374 }
22375 
22376 //A.I characters play the jump animation
common_jump()22377 void common_jump()
22378 {
22379     entity *dust;
22380 
22381     if(inair(self))
22382     {
22383         //printf("%f %f %f %d\n", self->base, self->position.y, self->velocity.y, self->landed_on_platform);
22384         return;
22385     }
22386 
22387     if(self->velocity.y <= 0) // wait if it is still go up
22388     {
22389         self->velocity.y = 0;
22390         self->position.y = self->base;
22391 
22392         self->jumping = 0;
22393         self->attacking = 0;
22394 
22395         if(!self->modeldata.runhold)
22396         {
22397             self->running = 0;
22398         }
22399 
22400         self->velocity.z = self->velocity.x = 0;
22401 
22402         if(validanim(self, ANI_JUMPLAND) && self->animation->landframe.frame == -1) // check if jumpland animation exists and not using landframe
22403         {
22404             self->takeaction = common_jumpland;
22405             ent_set_anim(self, ANI_JUMPLAND, 0);
22406             if(self->modeldata.dust.jump_land >= 0)
22407             {
22408                 dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_land, NULL);
22409                 if(dust)
22410                 {
22411                     dust->base = self->position.y;
22412                     dust->autokill = 2;
22413                     execute_onspawn_script(dust);
22414                 }
22415             }
22416         }
22417         else
22418         {
22419             if(self->modeldata.dust.jump_land >= 0 && self->animation->landframe.frame == -1)
22420             {
22421                 dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_land, NULL);
22422                 if(dust)
22423                 {
22424                     dust->base = self->position.y;
22425                     dust->autokill = 2;
22426                     execute_onspawn_script(dust);
22427                 }
22428             }
22429             if(self->animation->landframe.frame >= 0 && self->animating)
22430             {
22431                 return;
22432             }
22433 
22434             self->takeaction = NULL; // back to A.I. root
22435             set_idle(self);
22436         }
22437     }
22438 }
22439 
22440 //A.I. characters spawn
common_spawn()22441 void common_spawn()
22442 {
22443     self->idling = 0;
22444     if(self->animating)
22445     {
22446         return;
22447     }
22448     self->takeaction = NULL; // come to life
22449     set_idle(self);
22450 }
22451 
22452 //A.I. characters drop from the sky
common_drop()22453 void common_drop()
22454 {
22455     if(inair(self))
22456     {
22457         return;
22458     }
22459     self->idling = 1;
22460     self->takeaction = NULL;
22461     if(self->health <= 0)
22462     {
22463         kill(self);
22464     }
22465 }
22466 
22467 //Similar as above, walk off a wall/cliff
common_walkoff()22468 void common_walkoff()
22469 {
22470     if(inair(self) || self->animating)
22471     {
22472         return;
22473     }
22474     self->takeaction = NULL;
22475     set_idle(self);
22476 }
22477 
22478 // play turn animation and then flip
common_turn()22479 void common_turn()
22480 {
22481     if(!self->animating)
22482     {
22483         self->takeaction = NULL;
22484         self->velocity.x = self->velocity.z = 0;
22485         self->direction = !self->direction;
22486         set_idle(self);
22487     }
22488 }
22489 
22490 // switch to land animation, land safely
doland()22491 void doland()
22492 {
22493     self->velocity.x = self->velocity.z = 0;
22494     self->drop = 0;
22495     self->projectile = 0;
22496     self->damage_on_landing = 0;
22497     if(validanim(self, ANI_LAND))
22498     {
22499         self->takeaction = common_land;
22500         self->direction = !self->direction;
22501         ent_set_anim(self, ANI_LAND, 0);
22502     }
22503     else
22504     {
22505         self->takeaction = NULL;
22506         set_idle(self);
22507     }
22508 }
22509 
common_fall()22510 void common_fall()
22511 {
22512     // Still falling?
22513     if(self->falling ||  inair(self) || self->velocity.y)
22514     {
22515         return;
22516     }
22517 
22518 
22519     //self->velocity.x = self->velocity.z;
22520 
22521     // Landed
22522     if(self->projectile > 0)
22523     {
22524         if(self->projectile == 2)
22525         {
22526             // damage_on_landing==-2 means a player has pressed up+jump and has a land animation
22527             if((autoland == 1 && self->damage_on_landing == -1) || self->damage_on_landing == -2)
22528             {
22529                 // Added autoland option for landing
22530                 doland();
22531                 return;
22532             }
22533         }
22534         //self->projectile = 0;
22535         self->falling = 0;
22536     }
22537 
22538     // Drop Weapon due to Enemy Falling.
22539     //if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_KNOCKDOWN) dropweapon(1);
22540 
22541     if(self->boss && level_completed)
22542     {
22543         tospeedup = 1;
22544     }
22545 
22546     // Pause a bit...
22547     self->takeaction	= common_lie;
22548     self->stalltime		= time + MAX(0, (int)(self->staydown.rise + GAME_SPEED - self->modeldata.risetime.rise));	//Set rise delay.
22549     self->staydown.riseattack_stall	= time + MAX(0, (int)(self->staydown.riseattack - self->modeldata.risetime.riseattack));					//Set rise attack delay.
22550     self->staydown.rise = 0; //Reset staydown.
22551     self->staydown.riseattack = 0; //Reset staydown atk.
22552 }
22553 
common_try_riseattack()22554 void common_try_riseattack()
22555 {
22556     entity *target;
22557     if(!validanim(self, ANI_RISEATTACK))
22558     {
22559         return;
22560     }
22561 
22562     target = normal_find_target(ANI_RISEATTACK, 0);
22563     if(!target)
22564     {
22565         self->direction = !self->direction;
22566         target = normal_find_target(ANI_RISEATTACK, 0);
22567         self->direction = !self->direction;
22568     }
22569 
22570     if(target)
22571     {
22572         self->direction = (target->position.x > self->position.x);    // Stands up and swings in the right direction depending on chosen target
22573         set_riseattack(self, self->damagetype, 0);
22574     }
22575 }
22576 
common_lie()22577 void common_lie()
22578 {
22579     // Died?
22580     if(self->health <= 0)
22581     {
22582         if(self->modeldata.falldie == 2)
22583         {
22584             set_death(self, self->damagetype, 0);
22585         }
22586         if(!self->modeldata.nodieblink || (self->modeldata.nodieblink == 1 && !self->animating))
22587         {
22588             // Now have the option to blink or not
22589             self->takeaction = (self->modeldata.type & TYPE_PLAYER) ? player_blink : suicide;
22590             self->blink = 1;
22591             self->stalltime  = time + GAME_SPEED * 2;
22592         }
22593         else if(self->modeldata.nodieblink == 2  && !self->animating)
22594         {
22595             self->takeaction = (self->modeldata.type & TYPE_PLAYER) ? player_die : suicide;
22596 
22597         }
22598         else if(self->modeldata.nodieblink == 3  && !self->animating)
22599         {
22600             if(self->modeldata.type & TYPE_PLAYER)
22601             {
22602                 self->takeaction = player_die;
22603 
22604             }
22605             else
22606             {
22607                 self->modeldata.type = TYPE_NONE;
22608                 self->noaicontrol = 1;
22609             }
22610         }
22611 
22612         if (self->modeldata.maps.ko)   //Have a KO map?
22613         {
22614             if (self->modeldata.maps.kotype == KOMAP_TYPE_IMMEDIATELY || !self->animating)  //Wait for fall/death animation to finish?
22615             {
22616                 self->colourmap = model_get_colourmap(&(self->modeldata), self->modeldata.maps.ko);    //Apply map.
22617             }
22618         }
22619 
22620         return;
22621     }
22622 
22623     if(time < self->stalltime || self->position.y != self->base || self->velocity.y)
22624     {
22625         return;
22626     }
22627 
22628     //self->takeaction = common_rise;
22629     // Get up again
22630     //self->drop = 0;
22631     //self->falling = 0;
22632     //self->projectile = 0;
22633     //self->velocity.x = self->velocity.z = self->velocity.y = 0;
22634 
22635     set_rise(self, self->damagetype, 0);
22636 }
22637 
22638 // rise proc
common_rise()22639 void common_rise()
22640 {
22641     if(self->animating)
22642     {
22643         return;
22644     }
22645     self->takeaction = NULL;
22646     self->staydown.riseattack_stall = 0;	//Reset riseattack delay.
22647     if(self->modeldata.riseinv)
22648     {
22649         self->blink = self->modeldata.riseinv > 0;
22650         self->invinctime = time + ABS(self->modeldata.riseinv);
22651         self->invincible = 1;
22652     }
22653     set_idle(self);
22654 }
22655 
22656 // pain proc
common_pain()22657 void common_pain()
22658 {
22659     //self->velocity.x = self->velocity.z = 0; // complained
22660 
22661     if(self->animating || inair(self))
22662     {
22663         return;
22664     }
22665 
22666     self->inpain = 0;
22667     self->inbackpain = 0;
22668     if(self->link)
22669     {
22670 //        set_pain(self, -1, 0);
22671         self->takeaction = common_grabbed;
22672     }
22673     else if(self->blocking)
22674     {
22675         self->takeaction = common_block;
22676         ent_set_anim(self, ANI_BLOCK, 1);
22677     }
22678     else
22679     {
22680         self->takeaction = NULL;
22681         set_idle(self);
22682     }
22683 }
22684 
doprethrow()22685 void doprethrow()
22686 {
22687     entity *other = self->link;
22688     other->takeaction = common_prethrow;
22689     self->takeaction = common_throw_wait;
22690     self->velocity.x = self->velocity.z = self->velocity.y = other->velocity.x = other->velocity.z = other->velocity.y = 0;
22691     ent_set_anim(self, ANI_THROW, 0);
22692 }
22693 
22694 // 1 grabattack 2 grabforward 3 grabup 4 grabdown 5 grabbackward
22695 // other means grab finisher at once
dograbattack(int which)22696 void dograbattack(int which)
22697 {
22698     entity *other = self->link;
22699     self->takeaction = common_grabattack;
22700     self->attacking = 1;
22701     other->velocity.x = other->velocity.z = self->velocity.x = self->velocity.z = 0;
22702     if(which < 5 && which >= 0)
22703     {
22704         ++self->combostep[which];
22705         if(self->combostep[which] < 3 && validanim(self, grab_attacks[which][0]))
22706         {
22707             ent_set_anim(self, grab_attacks[which][0], 0);
22708         }
22709         else
22710         {
22711             memset(self->combostep, 0, sizeof(*self->combostep) * 5);
22712             if(validanim(self, grab_attacks[which][1]))
22713             {
22714                 ent_set_anim(self, grab_attacks[which][1], 0);
22715             }
22716             else if(validanim(self, ANI_ATTACK3))
22717             {
22718                 ent_set_anim(self, ANI_ATTACK3, 0);
22719             }
22720         }
22721     }
22722     else
22723     {
22724         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
22725         if(validanim(self, grab_attacks[0][1]))
22726         {
22727             ent_set_anim(self, grab_attacks[0][1], 0);
22728         }
22729         else if(validanim(self, ANI_ATTACK3))
22730         {
22731             ent_set_anim(self, ANI_ATTACK3, 0);
22732         }
22733     }
22734 }
22735 
common_grab_check()22736 void common_grab_check()
22737 {
22738     int rnum, which;
22739     entity *other = self->link;
22740 
22741     if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
22742     {
22743         return;
22744     }
22745 
22746     if(self->base != other->base)
22747     {
22748         // Change this from ->position.y to ->base
22749         self->takeaction = NULL;
22750         ent_unlink(self);
22751         set_idle(self);
22752         return;
22753     }
22754 
22755     if(!nolost && self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
22756     {
22757         dropweapon(1);
22758     }
22759 
22760     self->attacking = 0; //for checking
22761 
22762     rnum = rand32() & 31;
22763 
22764     if(time > self->releasetime)
22765     {
22766         if(rnum < 12)
22767         {
22768             // Release
22769             self->takeaction = NULL;
22770             ent_unlink(self);
22771             set_idle(self);
22772             return;
22773         }
22774         else
22775         {
22776             self->releasetime = time + (GAME_SPEED / 2);
22777         }
22778     }
22779 
22780     if(validanim(self, ANI_THROW) && rnum < 7)
22781     {
22782         if(self->modeldata.throwframewait >= 0)
22783         {
22784             doprethrow();
22785         }
22786         else
22787         {
22788             dothrow();
22789         }
22790         return;
22791     }
22792     //grab finisher
22793     if(rnum < 4)
22794     {
22795         dograbattack(-1);
22796         return;
22797     }
22798     which = rnum % 5;
22799     // grab attacks
22800     if(rnum > 12 && validanim(self, grab_attacks[which][0]))
22801     {
22802         dograbattack(which);
22803         return;
22804     }
22805 }
22806 
22807 //grabbing someone
common_grab()22808 void common_grab()
22809 {
22810     // if(self->link) return;
22811     if(self->link || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
22812     {
22813         return;
22814     }
22815 
22816     self->takeaction = NULL;
22817     self->attacking = 0;
22818     memset(self->combostep, 0, sizeof(*self->combostep) * 5);
22819     set_idle(self);
22820 }
22821 
22822 // being grabbed
common_grabbed()22823 void common_grabbed()
22824 {
22825     // Just check if we're still grabbed...
22826     if(self->link)
22827     {
22828         return;
22829     }
22830 
22831     self->stalltime = 0;
22832     self->takeaction = NULL;
22833     set_idle(self);
22834 }
22835 
22836 // picking up something
common_get()22837 void common_get()
22838 {
22839     if(self->animating)
22840     {
22841         return;
22842     }
22843 
22844     self->getting = 0;
22845     self->takeaction = NULL;
22846     set_idle(self);
22847 }
22848 
22849 // A.I. characters do the block
common_block()22850 void common_block()
22851 {
22852     int hb1 = self->modeldata.holdblock && (self->modeldata.type & TYPE_PLAYER) &&
22853               (!self->inpain || (self->modeldata.holdblock & 2)); //inpain = blockpain
22854     int hb2 = ((player + self->playerindex)->keys & FLAG_SPECIAL) ;
22855 
22856     if(self->inpain && (self->modeldata.holdblock & 2) && !self->animating && validanim(self, ANI_BLOCK))
22857     {
22858         self->inpain = 0;
22859         self->inbackpain = 0;
22860         ent_set_anim(self, ANI_BLOCK, 0);
22861     }
22862     else if(
22863         (hb1 && !hb2) ||
22864         (!self->animating && (!hb1 || !hb2))
22865     )
22866     {
22867         self->blocking = 0;
22868         self->takeaction = NULL;
22869         set_idle(self);
22870     }
22871 }
22872 
22873 
common_charge()22874 void common_charge()
22875 {
22876     if(self->animating)
22877     {
22878         return;
22879     }
22880 
22881     self->charging = 0;
22882     self->takeaction = NULL;
22883     set_idle(self);
22884 }
22885 
22886 
22887 // common code for entities hold an item
drop_item(entity * e)22888 entity *drop_item(entity *e)
22889 {
22890     s_spawn_entry p;
22891     entity *item;
22892     memset(&p, 0, sizeof(p));
22893 
22894     p.index = e->item;
22895     p.itemindex = p.weaponindex = -1;
22896     strcpy(p.alias, e->itemalias);
22897     p.position.y = e->position.y + 0.01; // for check, or an enemy "item" will drop from the sky
22898     p.health[0] = e->itemhealth;
22899     p.alpha = e->itemtrans;
22900     p.colourmap = e->itemmap;
22901     p.flip = e->direction;
22902 
22903     item = smartspawn(&p);
22904 
22905     if(item)
22906     {
22907         item->position.x = e->position.x;
22908         item->position.z = e->position.z;
22909         if(item->position.x < advancex)
22910         {
22911             item->position.x = advancex + 10;
22912         }
22913         else if(item->position.x > advancex + videomodes.hRes)
22914         {
22915             item->position.x = advancex + videomodes.hRes - 10;
22916         }
22917         if(!(level->scrolldir & (SCROLL_UP | SCROLL_DOWN)))
22918         {
22919             if(item->position.z - item->position.y < advancey)
22920             {
22921                 item->position.z = advancey + 10;
22922             }
22923             else if(item->position.z - item->position.y > advancey + videomodes.vRes)
22924             {
22925                 item->position.z = advancey + videomodes.vRes - 10;
22926             }
22927         }
22928         if(e->boss && (item->modeldata.type & TYPE_ENEMY))
22929         {
22930             item->boss = 1;
22931         }
22932     }
22933     return item;
22934 }
22935 
22936 //drop the driver, just spawn, dont takedamage
22937 // damage will adjust by the biker
drop_driver(entity * e)22938 entity *drop_driver(entity *e)
22939 {
22940     int i;
22941     s_spawn_entry p;
22942     entity *driver;
22943     memset(&p, 0, sizeof(p));
22944 
22945     if(e->modeldata.rider >= 0)
22946     {
22947         p.index = e->modeldata.rider;
22948     }
22949     else
22950     {
22951         return NULL;    // should not happen, just in case
22952     }
22953     /*p.x = e->position.x - advancex; p.z = e->position.z; */p.position.y = e->position.y + 10;
22954     p.itemindex = e->item;
22955     p.weaponindex = -1;
22956     strcpy(p.itemalias, e->itemalias);
22957     strcpy(p.alias, e->name);
22958     p.itemmap = e->itemmap;
22959     p.itemtrans = e->itemtrans;
22960     p.itemhealth = e->itemhealth;
22961     p.itemplayer_count = e->itemplayer_count;
22962     //p.colourmap = e->map;
22963     for(i = 0; i < MAX_PLAYERS; i++)
22964     {
22965         p.health[i] = e->modeldata.health;
22966     }
22967     p.boss = e->boss;
22968 
22969     driver = smartspawn(&p);
22970     if(driver)
22971     {
22972         driver->position.x = e->position.x;
22973         driver->position.z = e->position.z;
22974     }
22975     return driver;
22976 }
22977 
22978 
checkdeath()22979 void checkdeath()
22980 {
22981     if(self->health > 0)
22982     {
22983         return;
22984     }
22985     self->dead = 1;
22986     //be careful, since the opponent can be other types
22987     if(self->opponent && (self->opponent->modeldata.type & TYPE_PLAYER))
22988     {
22989         addscore(self->opponent->playerindex, self->modeldata.score);    // Add score to the player
22990     }
22991     self->nograb = 1;
22992     self->idling = 0;
22993 
22994     if(self->modeldata.diesound >= 0)
22995     {
22996         sound_play_sample(self->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100);
22997     }
22998 
22999     // drop item
23000     if(self->item && count_ents(TYPE_PLAYER) > self->itemplayer_count)
23001     {
23002         drop_item(self);
23003     }
23004 
23005     if(self->boss)
23006     {
23007         self->boss = 0;
23008         --level->bosses;
23009         if(!level->bosses && (self->modeldata.type & TYPE_ENEMY))
23010         {
23011             kill_all_enemies();
23012             level_completed = 1;
23013         }
23014     }
23015 }
23016 
checkdamageflip(entity * other,s_collision_attack * attack)23017 void checkdamageflip(entity *other, s_collision_attack *attack)
23018 {
23019     self->normaldamageflipdir = -1;
23020 
23021     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))))
23022     {
23023         return;
23024     }
23025 
23026     if(!self->frozen && !self->modeldata.noflip)// && !inair(self))
23027     {
23028         switch(attack->force_direction)
23029         {
23030             case DIRECTION_ADJUST_NONE:
23031                 if( !self->inbackpain )
23032                 {
23033                     if(self->position.x < other->position.x)
23034                     {
23035                         self->direction = DIRECTION_RIGHT;
23036                     }
23037                     else if(self->position.x > other->position.x)
23038                     {
23039                         self->direction = DIRECTION_LEFT;
23040                     }
23041                 }
23042                 else
23043                 {
23044                     if(self->position.x < other->position.x)
23045                     {
23046                         self->normaldamageflipdir = DIRECTION_RIGHT;
23047                     }
23048                     else if(self->position.x > other->position.x)
23049                     {
23050                         self->normaldamageflipdir = DIRECTION_LEFT;
23051                     }
23052                 }
23053                 break;
23054 
23055             case DIRECTION_ADJUST_SAME:
23056 
23057                 self->direction = other->direction;
23058                 break;
23059 
23060             case DIRECTION_ADJUST_OPPOSITE:
23061 
23062                 self->direction = !other->direction;
23063                 break;
23064 
23065             case DIRECTION_ADJUST_RIGHT:
23066 
23067                 self->direction = DIRECTION_RIGHT;
23068                 break;
23069 
23070             case DIRECTION_ADJUST_LEFT:
23071 
23072                 self->direction = DIRECTION_LEFT;
23073                 break;
23074         }
23075     }
23076 }
23077 
checkdamageeffects(s_collision_attack * attack)23078 void checkdamageeffects(s_collision_attack *attack)
23079 {
23080 #define _freeze         attack->freeze
23081 #define _maptime        attack->maptime
23082 #define _freezetime     attack->freezetime
23083 #define _remap          attack->forcemap
23084 #define _blast          attack->blast
23085 #define _steal          attack->steal
23086 #define _seal           attack->seal
23087 #define _sealtime       attack->sealtime
23088 #define _dot            attack->recursive->mode
23089 #define _dot_index      attack->recursive->index
23090 #define _dot_time       attack->recursive->time
23091 #define _dot_force      attack->recursive->force
23092 #define _dot_rate       attack->recursive->rate
23093 #define _staydown0      attack->staydown.rise
23094 #define _staydown1		attack->staydown.riseattack
23095 
23096     entity *opp = self->opponent;
23097 
23098     if(_steal && opp && opp != self)
23099     {
23100         if(self->health >= attack->attack_force)
23101         {
23102             opp->health += attack->attack_force;
23103         }
23104         else
23105         {
23106             opp->health += self->health;
23107         }
23108         if(opp->health > opp->modeldata.health)
23109         {
23110             opp->health = opp->modeldata.health;
23111         }
23112     }
23113     if(_freeze && !self->frozen)// && !self->owner && !self->modeldata.nomove)
23114     {
23115         // New freeze attack - If not frozen, freeze entity
23116         self->frozen = 1;
23117         if(self->freezetime == 0)
23118         {
23119             self->freezetime = time + _freezetime;
23120         }
23121         if(_remap == -1 && self->modeldata.maps.frozen != -1)
23122         {
23123             self->colourmap = model_get_colourmap(&(self->modeldata), self->modeldata.maps.frozen);    //12/14/2007 Damon Caskey: If opponents frozen map = -1 or only stun, then don't change the color map.
23124         }
23125         self->drop = 0;
23126     }
23127     else if(self->frozen)
23128     {
23129         unfrozen(self);
23130         self->drop = 1;
23131     }
23132 
23133     if(_remap > 0 && !_freeze)
23134     {
23135         self->maptime = time + _maptime;
23136         self->colourmap = model_get_colourmap(&(self->modeldata), _remap);
23137     }
23138 
23139     if(_seal)                                                                       //Sealed: Disable special moves.
23140     {
23141         self->sealtime  = time + _sealtime;                                         //Set time to apply seal. No specials for you!
23142         self->seal      = _seal;                                                    //Set seal. Any animation with energycost > seal is disabled.
23143     }
23144 
23145     if(attack->recursive)
23146     {
23147         if(_dot)                                                                        //dot: Damage over time effect.
23148         {
23149             self->dot_owner[_dot_index] = opp ? opp : self;			                    //dot owner.
23150             self->dot[_dot_index]       = _dot;                                         //Mode: 1. HP (non lethal), 2. MP, 3. HP (non lethal) & MP, 4. HP, 5. HP & MP.
23151             self->dot_time[_dot_index]  = time + (_dot_time * GAME_SPEED / 100);        //Gametime dot will expire.
23152             self->dot_force[_dot_index] = _dot_force;                                   //How much to dot each tick.
23153             self->dot_rate[_dot_index]  = _dot_rate;                                    //Delay between dot ticks.
23154             self->dot_atk[_dot_index]   = attack->attack_type;                          //dot attack type.
23155         }
23156     }
23157 
23158 
23159 
23160     if(self->modeldata.nodrop)
23161     {
23162         self->drop = 0;    // Static enemies/nodrop enemies cannot be knocked down
23163     }
23164 
23165     if(inair(self) && !self->frozen && self->modeldata.nodrop < 2)
23166     {
23167         self->drop = 1;
23168     }
23169 
23170     if(attack->no_pain)
23171     {
23172         self->drop = 0;
23173     }
23174 
23175     self->projectile = _blast;
23176 
23177     if(self->drop)
23178     {
23179         self->staydown.rise	= _staydown0;                                            //Staydown: Add to risetime until next rise.
23180         self->staydown.riseattack   = _staydown1;
23181     }
23182 
23183 #undef _freeze
23184 #undef _maptime
23185 #undef _freezetime
23186 #undef _remap
23187 #undef _blast
23188 #undef _steal
23189 #undef _seal
23190 #undef _sealtime
23191 #undef _dot
23192 #undef _dot_index
23193 #undef _dot_time
23194 #undef _dot_force
23195 #undef _dot_rate
23196 #undef _staydown0
23197 #undef _staydown1
23198 }
23199 
checkdamagedrop(s_collision_attack * attack)23200 void checkdamagedrop(s_collision_attack *attack)
23201 {
23202     int attackdrop = attack->attack_drop;
23203     float fdefense_knockdown = self->defense[attack->attack_type].knockdown;
23204     if(self->modeldata.animal)
23205     {
23206         self->drop = 1;
23207     }
23208     if(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0)
23209     {
23210         attackdrop = 0;    //guardbreak does not knock down.
23211     }
23212     if(self->drop || attack->no_pain)
23213     {
23214         return;    // just in case, if we already fall, dont check fall again
23215     }
23216     // reset count if knockdowntime expired.
23217     if(self->knockdowntime && self->knockdowntime < time)
23218     {
23219         self->knockdowncount = self->modeldata.knockdowncount;
23220     }
23221 
23222     self->knockdowncount -= (attackdrop * fdefense_knockdown);
23223     self->knockdowntime = time + GAME_SPEED;
23224     self->drop = (self->knockdowncount < 0); // knockdowncount < 0 means knocked down
23225 }
23226 
checkmpadd()23227 void checkmpadd()
23228 {
23229     entity *other = self->opponent;
23230     if(other == NULL || other == self)
23231     {
23232         return;
23233     }
23234 
23235     if(magic_type == 1 )
23236     {
23237         other->mp += other->modeldata.mprate;
23238 
23239         if(other->mp > other->modeldata.mp)
23240         {
23241             other->mp = other->modeldata.mp;
23242         }
23243         else if(other->mp < 0)
23244         {
23245             other->mp = 0;
23246         }
23247     }
23248 }
23249 
checkhitscore(entity * other,s_collision_attack * attack)23250 void checkhitscore(entity *other, s_collision_attack *attack)
23251 {
23252     entity *opp = self->opponent;
23253     if(!opp)
23254     {
23255         return;
23256     }
23257     if(opp && opp != self && (opp->modeldata.type & TYPE_PLAYER))
23258     {
23259         // Added obstacle so explosions can hurt enemies
23260         addscore(opp->playerindex, attack->attack_force * self->modeldata.multiple);  // New multiple variable
23261         control_rumble(opp->playerindex, attack->attack_force * 2);
23262     }
23263     // Don't animate or fall if hurt by self, since
23264     // it means self fell to the ground already. :)
23265     // Add throw score to the player
23266     else if(other == self && self->damage_on_landing > 0)
23267     {
23268         addscore(opp->playerindex, attack->attack_force);
23269     }
23270 }
23271 
checkdamage(entity * other,s_collision_attack * attack)23272 void checkdamage(entity *other, s_collision_attack *attack)
23273 {
23274     int force = attack->attack_force;
23275     int type = attack->attack_type;
23276 
23277     if(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0)
23278     {
23279         force = 0;    //guardbreak does not deal damage.
23280     }
23281     if(type >= 0 && type < max_attack_types)
23282     {
23283         force = (int)(force * other->offense_factors[type]);
23284         force = (int)(force * self->defense[type].factor);
23285     }
23286 
23287     self->health -= force; //Apply damage.
23288 
23289     if (self->health > self->modeldata.health)
23290     {
23291         self->health = self->modeldata.health;    //Cap negative damage to max health.
23292     }
23293 
23294     if(attack->no_kill && self->health <= 0)
23295     {
23296         self->health = 1;
23297     }
23298 
23299     execute_takedamage_script(self, other, force, attack->attack_drop, type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, attack->tag);                       //Execute the take damage script.
23300 
23301     if (self->health <= 0)                                      //Health at 0?
23302     {
23303         if(!(self->position.y < PIT_DEPTH || self->lifespancountdown < 0)) //Not a pit death or countdown?
23304         {
23305             if (self->invincible == 2)                          //Invincible type 2?
23306             {
23307                 self->health = 1;                               //Stop at 1hp.
23308             }
23309             else if(self->invincible == 3)                      //Invincible type 3?
23310             {
23311                 self->health = self->modeldata.health;          //Reset to max health.
23312             }
23313         }
23314         execute_ondeath_script(self, other, force, attack->attack_drop, type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, attack->tag);   //Execute ondeath script.
23315     }
23316 }
23317 
checkgrab(entity * other,s_collision_attack * attack)23318 int checkgrab(entity *other, s_collision_attack *attack)
23319 {
23320     //if(attack->no_pain) return  0; //no effect, let modders to deside, don't bother check it here
23321     if(self != other && attack->grab && cangrab(other, self))
23322     {
23323         if(adjust_grabposition(other, self, attack->grab_distance, attack->grab))
23324         {
23325             ents_link(other, self);
23326             self->position.y = other->position.y;
23327         }
23328         else
23329         {
23330             return 0;
23331         }
23332     }
23333     return 1;
23334 }
23335 
arrow_takedamage(entity * other,s_collision_attack * attack)23336 int arrow_takedamage(entity *other, s_collision_attack *attack)
23337 {
23338     self->modeldata.no_adjust_base = 0;
23339     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;
23340     if( common_takedamage(other, attack) && self->dead)
23341     {
23342         return 1;
23343     }
23344     return 0;
23345 }
23346 
common_takedamage(entity * other,s_collision_attack * attack)23347 int common_takedamage(entity *other, s_collision_attack *attack)
23348 {
23349     if(self->dead)
23350     {
23351         return 0;
23352     }
23353     if(self->toexplode == 2)
23354     {
23355         return 0;
23356     }
23357     // fake 'grab', if failed, return as the attack hit nothing
23358     if(!checkgrab(other, attack))
23359     {
23360         return 0;    // try to grab but failed, so return 0 means attack missed
23361     }
23362 
23363     // set pain_time so it wont get hit too often
23364     // 2011/11/24 UT: move this to do_attack to merge with block code
23365     //self->pain_time = time + (attack->pain_time?attack->pain_time:(GAME_SPEED / 5));
23366     // set oppoent
23367     if(self != other)
23368     {
23369         set_opponent(self, other);
23370     }
23371     // adjust type
23372     if(attack->attack_type >= 0 && attack->attack_type < max_attack_types)
23373     {
23374         self->damagetype = attack->attack_type;
23375     }
23376     else
23377     {
23378         self->damagetype = ATK_NORMAL;
23379     }
23380     // pre-check drop
23381     checkdamagedrop(attack);
23382     // Drop Weapon due to being hit.
23383     if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
23384     {
23385         dropweapon(1);
23386     }
23387     // check effects, e.g., frozen, blast, steal
23388     if(!(self->modeldata.guardpoints.max > 0 && self->modeldata.guardpoints.current <= 0))
23389     {
23390         checkdamageeffects(attack);
23391     }
23392     // check backpain
23393     check_backpain(other,self);
23394     // check flip direction
23395     checkdamageflip(other, attack);
23396     // mprate can also control the MP recovered per hit.
23397     checkmpadd();
23398     //damage score
23399     checkhitscore(other, attack);
23400     // check damage, cost hp.
23401     checkdamage(other, attack);
23402     // is it dead now?
23403     checkdeath();
23404 
23405     if(self->modeldata.type & TYPE_PLAYER)
23406     {
23407         control_rumble(self->playerindex, attack->attack_force * 3);
23408     }
23409     if(self->position.y <= PIT_DEPTH && self->dead)
23410     {
23411         if(self->modeldata.type & TYPE_PLAYER)
23412         {
23413             player_die();
23414         }
23415         else
23416         {
23417             kill(self);
23418         }
23419         return 1;
23420     }
23421     // fall to the ground so dont fall again
23422     if(self->damage_on_landing)
23423     {
23424         self->damage_on_landing = 0;
23425         return 1;
23426     }
23427     // unlink due to being hit
23428     if((self->opponent && self->opponent->grabbing != self) ||
23429             self->dead || self->frozen || self->drop)
23430     {
23431         ent_unlink(self);
23432     }
23433     // Enemies can now use SPECIAL2 to escape cheap attack strings!
23434     if(self->modeldata.escapehits)
23435     {
23436         if(self->drop)
23437         {
23438             self->escapecount = 0;
23439         }
23440         else
23441         {
23442             self->escapecount++;
23443         }
23444     }
23445     // New pain, fall, and death animations. Also, the nopain flag.
23446     if(self->drop || self->health <= 0)
23447     {
23448         self->takeaction = common_fall;
23449         // Drop Weapon due to death.
23450         if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_DEATH && self->health <= 0)
23451         {
23452             dropweapon(1);
23453         }
23454         else if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_KNOCKDOWN)
23455         {
23456             dropweapon(1);
23457         }
23458 
23459         if(self->health <= 0 && self->modeldata.falldie == 1)
23460         {
23461             self->velocity.x = self->velocity.z = self->velocity.y = 0;
23462             set_death(self, self->damagetype, 0);
23463         }
23464         else
23465         {
23466             self->velocity.x = attack->dropv.x;
23467             self->velocity.z = attack->dropv.z;
23468             if(self->direction == DIRECTION_RIGHT)
23469             {
23470                 self->velocity.x = -self->velocity.x;
23471             }
23472             if(self->inbackpain) self->velocity.x *= -1;
23473             toss(self, attack->dropv.y);
23474             self->damage_on_landing = attack->damage_on_landing;
23475             self->knockdowncount = self->modeldata.knockdowncount; // reset the knockdowncount
23476             self->knockdowntime = 0;
23477 
23478             // Now if no fall/die animations exist, entity simply disapears
23479             //set_fall(entity *iFall, int type, int reset, entity* other, int force, int drop)
23480             if(!set_fall(self, self->damagetype, 1, other, attack->attack_force, attack->attack_drop, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add, attack->tag))
23481             {
23482                 if(self->modeldata.type & TYPE_PLAYER)
23483                 {
23484                     player_die();
23485                 }
23486                 else
23487                 {
23488                     kill(self);
23489                 }
23490                 return 1;
23491             }
23492         }
23493         if(self->modeldata.type & TYPE_PLAYER)
23494         {
23495             control_rumble(self->playerindex, attack->attack_force * 3);
23496         }
23497     }
23498     else if(attack->grab && !attack->no_pain)
23499     {
23500         self->takeaction = common_pain;
23501         other->takeaction = common_grabattack;
23502         other->stalltime = time + GRAB_STALL;
23503         self->releasetime = time + (GAME_SPEED / 2);
23504         set_pain(self, self->damagetype, 0);
23505     }
23506     // Don't change to pain animation if frozen
23507     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))
23508     {
23509         self->takeaction = common_pain;
23510         set_pain(self, self->damagetype, 1);
23511     }
23512     return 1;
23513 }
23514 
23515 // A.I. try upper cut
common_try_upper(entity * target)23516 int common_try_upper(entity *target)
23517 {
23518     if(!validanim(self, ANI_UPPER))
23519     {
23520         return 0;
23521     }
23522 
23523 
23524     if(!target)
23525     {
23526         target = normal_find_target(ANI_UPPER, 0);
23527     }
23528 
23529     // Target jumping? Try uppercut!
23530     if(target && target->jumping )
23531     {
23532         self->takeaction = common_attack_proc;
23533         set_attacking(self);
23534         self->velocity.z = self->velocity.x = 0;
23535         // Don't waste any time!
23536         ent_set_anim(self, ANI_UPPER, 0);
23537         return 1;
23538     }
23539     return 0;
23540 }
23541 
common_try_runattack(entity * target)23542 int common_try_runattack(entity *target)
23543 {
23544     if(!self->running || !validanim(self, ANI_RUNATTACK))
23545     {
23546         return 0;
23547     }
23548 
23549 
23550     if(!target)
23551     {
23552         target = normal_find_target(ANI_RUNATTACK, 0);
23553     }
23554 
23555     if(target)
23556     {
23557         if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking))
23558         {
23559             return 0;
23560         }
23561         self->takeaction = common_attack_proc;
23562         self->velocity.z = self->velocity.x = 0;
23563         set_attacking(self);
23564         ent_set_anim(self, ANI_RUNATTACK, 0);
23565         return 1;
23566     }
23567     return 0;
23568 }
23569 
common_try_block(entity * target)23570 int common_try_block(entity *target)
23571 {
23572     if(self->modeldata.nopassiveblock == 0 ||
23573             (rand32()&self->modeldata.blockodds) != 1 ||
23574             !validanim(self, ANI_BLOCK))
23575     {
23576         return 0;
23577     }
23578 
23579     if(!target)
23580     {
23581         target = block_find_target(ANI_BLOCK, 0);    // temporary fix, other wise ranges never work
23582     }
23583 
23584     // no passive block, so block by himself :)
23585     if(target && target->attacking)
23586     {
23587         self->takeaction = common_block;
23588         set_blocking(self);
23589         self->velocity.z = self->velocity.x = 0;
23590         ent_set_anim(self, ANI_BLOCK, 0);
23591         return 1;
23592     }
23593     return 0;
23594 }
23595 
23596 // this logic could be used for multiple times, so make a function
23597 // pick a random attack or return the first attacks if testonly is set
23598 // when testonly is set, the function will not check special attacks (upper, jumpattack)
23599 // if target is NULL, ranges are not checked
pick_random_attack(entity * target,int testonly)23600 int pick_random_attack(entity *target, int testonly)
23601 {
23602     int found = 0, i, j;
23603 
23604     for(i = 0; i < max_attacks; i++) // TODO: recheck range for attacks chains
23605     {
23606         if(validanim(self, animattacks[i]) &&
23607                 (!target || check_range(self, target, animattacks[i])))
23608         {
23609             for(j = ((5 - i) >= 0 ? (5 - i) : 0) * 3; j >= 0; j--)
23610             {
23611                 atkchoices[found++] = animattacks[i];
23612             }
23613         }
23614     }
23615     for(i = 0; i < max_freespecials; i++)
23616     {
23617         if(validanim(self, animspecials[i]) &&
23618                 (check_energy(COST_CHECK_MP, animspecials[i]) ||
23619                  check_energy(COST_CHECK_HP, animspecials[i])) &&
23620                 (!target || check_range(self, target, animspecials[i])))
23621         {
23622             atkchoices[found++] = animspecials[i];
23623         }
23624     }
23625     if( validanim(self, ANI_THROWATTACK) &&
23626             self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
23627             (!target || check_range(self, target, ANI_THROWATTACK) ))
23628     {
23629         atkchoices[found++] = ANI_THROWATTACK;
23630     }
23631 
23632     if(testonly)
23633     {
23634         if(found)
23635         {
23636             return atkchoices[(rand32() & 0xffff) % found];
23637         }
23638         return -1;
23639     }
23640 
23641     if( validanim(self, ANI_JUMPATTACK) &&
23642             (!target || check_range(self, target, ANI_JUMPATTACK)) )
23643     {
23644         if(testonly)
23645         {
23646             return ANI_JUMPATTACK;
23647         }
23648         atkchoices[found++] = ANI_JUMPATTACK;
23649     }
23650     if( validanim(self, ANI_UPPER) &&
23651             (!target || check_range(self, target, ANI_UPPER)) )
23652     {
23653         if(testonly)
23654         {
23655             return ANI_UPPER;
23656         }
23657         atkchoices[found++] = ANI_UPPER;
23658     }
23659 
23660     if(found)
23661     {
23662         return atkchoices[(rand32() & 0xffff) % found];
23663     }
23664 
23665     return -1;
23666 }
23667 
23668 
23669 // code to lower the chance of attacks, may change while testing old mods
23670 // min - min attack chance
23671 // max - max attack chance
check_attack_chance(entity * target,float min,float max)23672 int check_attack_chance(entity *target, float min, float max)
23673 {
23674 
23675     float chance, chance1, chance2;//, aggfix;
23676 
23677     if(self->modeldata.aiattack & AIATTACK1_ALWAYS)
23678     {
23679         return 1;
23680     }
23681 
23682     chance1 = MIN(1.0f, (diff(self->position.x, self->destx) + diff(self->position.z, self->destz)) / (videomodes.hRes + videomodes.vRes) * move_noatk_factor);
23683     chance2 = MIN(1.0f, (count_ents(self->modeldata.type) - 1) * group_noatk_factor);
23684 
23685     chance = (1.0f - chance1) * (1.0f - chance2);
23686 
23687     if(chance > max)
23688     {
23689         chance = max;
23690     }
23691     else if(chance < min)
23692     {
23693         chance = min;
23694     }
23695 
23696     chance *= (1.0 - self->modeldata.attackthrottle);
23697 
23698     if(self->position.x < screenx - 10 || self->position.x > screenx + videomodes.hRes + 10)
23699     {
23700         if (self->modeldata.offscreen_noatk_factor != 0) chance *= (1.0 - self->modeldata.offscreen_noatk_factor);
23701         else chance *= (1.0 - offscreen_noatk_factor);
23702     }
23703 
23704     return (randf(1) <= chance);
23705 }
23706 
23707 //make a function, mostly for debug purpose
23708 //give it a chance to reset current noattack timer
recheck_nextattack(entity * target)23709 u32 recheck_nextattack(entity *target)
23710 {
23711     if(target->blocking)
23712     {
23713         self->nextattack = 0;
23714     }
23715     else if(target->attacking && self->nextattack > 4)
23716     {
23717         self->nextattack -= 4;
23718     }
23719     else if(target->jumping && self->nextattack > 16)
23720     {
23721         self->nextattack -= 16;
23722     }
23723 
23724     return self->nextattack;
23725 }
23726 
common_try_normalattack(entity * target)23727 int common_try_normalattack(entity *target)
23728 {
23729     target = normal_find_target(-1, 0);
23730 
23731     if(!target)
23732     {
23733         return 0;
23734     }
23735 
23736     recheck_nextattack(target);
23737 
23738     if(recheck_nextattack(target) > time)
23739     {
23740         return 0;
23741     }
23742 
23743     if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking || target->takeaction == common_rise))
23744     {
23745         return 0;
23746     }
23747 
23748     if(pick_random_attack(target, 1) >= 0)
23749     {
23750         if(self->combostep[0] && self->combotime > time)
23751         {
23752             self->stalltime = time + 1;
23753         }
23754         else
23755         {
23756             if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance))
23757             {
23758                 self->nextattack = time + randf(self->modeldata.attackthrottletime);
23759                 return 0;
23760             }
23761             else
23762             {
23763                 self->stalltime = time + (int)randf((float)MAX(1, GAME_SPEED * 3 / 4 - self->modeldata.aggression));
23764             }
23765         }
23766         self->takeaction = normal_prepare;
23767         self->velocity.z = self->velocity.x = 0;
23768         set_idle(self);
23769         self->idling = 0; // not really idle, in fact it is thinking
23770         self->attacking = -1; // pre-attack, for AI-block check
23771         return 1;
23772     }
23773 
23774     return 0;
23775 }
23776 
common_try_jumpattack(entity * target)23777 int common_try_jumpattack(entity *target)
23778 {
23779     entity *dust;
23780     int rnum, ani = 0;
23781     if((validanim(self, ANI_JUMPATTACK) || validanim(self, ANI_JUMPATTACK2)))
23782     {
23783         if(!validanim(self, ANI_JUMPATTACK))
23784         {
23785             rnum = 1;
23786         }
23787         else if(validanim(self, ANI_JUMPATTACK2) && (rand32() & 1))
23788         {
23789             rnum = 1;
23790         }
23791         else
23792         {
23793             rnum = 0;
23794         }
23795 
23796         if(rnum == 0 &&
23797                 // do a jumpattack
23798                 (target || (target = normal_find_target(ANI_JUMPATTACK, 0))) )
23799         {
23800             if(recheck_nextattack(target) > time)
23801             {
23802                 return 0;
23803             }
23804 
23805             if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking))
23806             {
23807                 rnum = -1;
23808             }
23809             else
23810             {
23811                 if(!check_attack_chance(target, 0.05f, 0.4f))
23812                 {
23813                     self->nextattack = time + randf(self->modeldata.attackthrottletime);
23814                     return 0;
23815                 }
23816                 //ent_set_anim(self, ANI_JUMPATTACK, 0);
23817                 ani = ANI_JUMPATTACK;
23818                 if(self->direction == DIRECTION_RIGHT)
23819                 {
23820                     self->velocity.x = (float)1.3;
23821                 }
23822                 else
23823                 {
23824                     self->velocity.x = (float) - 1.3;
23825                 }
23826                 self->velocity.z = 0;
23827             }
23828         }
23829         else if(rnum == 1 &&
23830                 // do a jumpattack2
23831                 (target || (target = normal_find_target(ANI_JUMPATTACK2, 0))) )
23832         {
23833             if(recheck_nextattack(target) > time)
23834             {
23835                 return 0;
23836             }
23837 
23838             if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking))
23839             {
23840                 rnum = -1;
23841             }
23842             else
23843             {
23844                 if(!check_attack_chance(target, 0.05f, 0.5f))
23845                 {
23846                     self->nextattack = time + randf(self->modeldata.attackthrottletime);
23847                     return 0;
23848                 }
23849                 //ent_set_anim(self, ANI_JUMPATTACK2, 0);
23850                 ani = ANI_JUMPATTACK2;
23851                 self->velocity.x = self->velocity.z = 0;
23852             }
23853         }
23854         else
23855         {
23856             rnum = -1;
23857         }
23858 
23859         if(rnum >= 0)
23860         {
23861 
23862             self->takeaction = common_jump;
23863             set_attacking(self);
23864             self->jumping = 1;
23865             toss(self, self->modeldata.jumpheight);
23866 
23867             if(self->modeldata.dust.jump_start >= 0)
23868             {
23869                 dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_start, NULL);
23870                 if(dust)
23871                 {
23872                     dust->base = self->position.y;
23873                     dust->autokill = 2;
23874                     execute_onspawn_script(dust);
23875                 }
23876             }
23877 
23878             ent_set_anim(self, ani, 0);
23879 
23880             return 1;
23881         }
23882     }
23883     return 0;
23884 }
23885 
common_try_grab(entity * other)23886 int common_try_grab(entity *other)
23887 {
23888     int trygrab(entity * t);
23889 
23890     // old rand()
23891     if( (rand32() & 7) == 0 &&
23892             (validanim(self, ANI_THROW) ||
23893              validanim(self, ANI_GRAB)) && self->idling &&
23894             (other || (other = find_ent_here(self, self->position.x, self->position.z, self->modeldata.hostile, NULL))) &&
23895             trygrab(other))
23896     {
23897         return 1;
23898     }
23899     return 0;
23900 }
23901 
23902 // Normal attack style
23903 // Used by root A.I., what to do if a target is found.
23904 // return 0 if no action is token
23905 // return 1 if an action is token
normal_attack()23906 int normal_attack()
23907 {
23908     //int rnum;
23909 
23910     //rnum = rand32()&7;
23911     if( common_try_grab(NULL) ||
23912             common_try_upper(NULL) ||
23913             common_try_block(NULL) ||
23914             common_try_runattack(NULL) ||
23915             //(rnum < 2 && common_try_freespecial(NULL)) ||
23916             common_try_normalattack(NULL) ||
23917             common_try_jumpattack(NULL) )
23918     {
23919         self->running = 0;
23920         return 1;
23921     }
23922     return 0;// nothing to do? so go to next think step
23923 }
23924 
23925 // A.I. characters do a throw
common_throw()23926 void common_throw()
23927 {
23928     if(self->animating)
23929     {
23930         return;    // just play the throw animation
23931     }
23932 
23933     // we have done the throw, return to A.I. root
23934     self->takeaction = NULL;
23935 
23936     set_idle(self);
23937 }
23938 
23939 // toss the grabbed one
dothrow()23940 void dothrow()
23941 {
23942     entity *other;
23943     self->velocity.x = self->velocity.z = 0;
23944     other = self->link;
23945     if(other == NULL) //change back to idle, or we will get stuck here
23946     {
23947         self->takeaction = NULL;// A.I. root again
23948         set_idle(self);
23949         return;
23950     }
23951 
23952     if(other->modeldata.throwheight)
23953     {
23954         toss(other, other->modeldata.throwheight);
23955     }
23956     else
23957     {
23958         toss(other, other->modeldata.jumpheight);
23959     }
23960 
23961     other->direction = self->direction;
23962     other->projectile = 2;
23963     other->velocity.x = (other->direction == DIRECTION_RIGHT) ? (-other->modeldata.throwdist) : (other->modeldata.throwdist);
23964 
23965     if(autoland == 1 && validanim(other, ANI_LAND))
23966     {
23967         other->damage_on_landing = -1;
23968     }
23969     else
23970     {
23971         other->damage_on_landing = self->modeldata.throwdamage;
23972     }
23973 
23974     ent_unlink(other);
23975 
23976     other->takeaction = common_fall;
23977     self->takeaction = common_throw;
23978     set_fall(other, ATK_NORMAL, 0, self, 0, 0, 0, 0, 0, 0, 0);
23979     ent_set_anim(self, ANI_THROW, 0);
23980 }
23981 
23982 
23983 // Waiting until throw frame reached
common_throw_wait()23984 void common_throw_wait()
23985 {
23986     if(!self->link)
23987     {
23988         self->takeaction = NULL;// A.I. root again
23989         set_idle(self);
23990         return;
23991     }
23992 
23993     self->releasetime += THINK_SPEED; //extend release time
23994 
23995     if(self->animpos != self->modeldata.throwframewait)
23996     {
23997         return;
23998     }
23999 
24000     dothrow();
24001 }
24002 
24003 
common_prethrow()24004 void common_prethrow()
24005 {
24006     self->running = 0;    // Quits running if grabbed by opponent
24007 
24008     // Just check if we're still grabbed...
24009     if(self->link)
24010     {
24011         return;
24012     }
24013 
24014     self->takeaction = NULL;// A.I. root again
24015 
24016     set_idle(self);
24017 }
24018 
24019 // warp to its parent entity, just like skeletons in Diablo 2
npc_warp()24020 void npc_warp()
24021 {
24022     if(!self->parent)
24023     {
24024         return;
24025     }
24026     self->position.z = self->parent->position.z;
24027     self->position.x = self->parent->position.x;
24028     self->position.y = self->parent->position.y;
24029     self->velocity.x = self->velocity.z = 0;
24030     self->base = self->parent->base;
24031     self->velocity.y = 0;
24032 
24033     if(validanim(self, ANI_RESPAWN))
24034     {
24035         self->takeaction = common_spawn;
24036         ent_set_anim(self, ANI_RESPAWN, 0);
24037     }
24038     else if(validanim(self, ANI_SPAWN))
24039     {
24040         self->takeaction = common_spawn;
24041         ent_set_anim(self, ANI_SPAWN, 0);
24042     }
24043 }
24044 
adjust_grabposition(entity * ent,entity * other,float dist,int grabin)24045 int adjust_grabposition(entity *ent, entity *other, float dist, int grabin)
24046 {
24047     float x1, z1, x2, z2, x;
24048 
24049     //if(ent->position.y != other->position.y)
24050     if(diff(ent->position.y,other->position.y) > T_WALKOFF)
24051     {
24052         return 0;
24053     }
24054     //if(ent->base != other->base)
24055     if(diff(ent->base,other->base) > T_WALKOFF)
24056     {
24057         return 0;
24058     }
24059 
24060     if(grabin == 1)
24061     {
24062         x1 = ent->position.x;
24063         z1 = z2 = ent->position.z;
24064         x2 = ent->position.x + ((other->position.x > ent->position.x) ? dist : -dist);
24065     }
24066     else
24067     {
24068         x = (ent->position.x + other->position.x) / 2;
24069         x1 = x + ((ent->position.x >= other->position.x) ? (dist / 2) : (-dist / 2));
24070         x2 = x + ((other->position.x > ent->position.x) ? (dist / 2) : (-dist / 2));
24071         z1 = z2 = (ent->position.z + other->position.z) / 2;
24072     }
24073 
24074     if(0 >= testmove(ent, ent->position.x, ent->position.z, x1, z1) || 0 >= testmove(other, other->position.x, other->position.z, x2, z2))
24075     {
24076         return 0;
24077     }
24078 
24079     ent->position.x = x1;
24080     ent->position.z = z1;
24081     other->position.x = x2;
24082     other->position.z = z2;
24083     //other->position.y = ent->position.y;
24084     //other->base = ent->base;
24085     return 1;
24086 }
24087 
dograb(entity * attacker,entity * target,e_dograb_adjustcheck adjustcheck)24088 int dograb(entity *attacker, entity *target, e_dograb_adjustcheck adjustcheck)
24089 {
24090     /*
24091     Execute grab action. Added by splitting off trygrab
24092     so entities can be forced to perform grab by script.
24093     Damon V. Caskey
24094     2013-12-30
24095 
24096     Attacker: Entity performing grab.
24097     target, entity being grabbed.
24098     */
24099 
24100     int result  = 0; //Output value.
24101     int pass    = 1; //Adjust pass/fail.
24102 
24103     /* If an adjust check is needed, make sure adjusting did not fail. */
24104     if(adjustcheck == DOGRAB_ADJUSTCHECK_TRUE)
24105     {
24106         pass = adjust_grabposition(attacker, target, attacker->modeldata.grabdistance, 0);
24107     }
24108 
24109     /* If adjust_grabposition passed (or wasn't needed) perform grab actions. */
24110     if(pass)
24111     {
24112         if(attacker->model->grabflip & 1)
24113         {
24114             attacker->direction = (attacker->position.x < target->position.x);
24115         }
24116 
24117         /* Set flags. */
24118         set_opponent(target, attacker);
24119         ents_link(attacker, target);
24120         target->attacking = 0;
24121         attacker->idling = 0;
24122         attacker->running = 0;
24123 
24124         /* Stop all movement. */
24125         attacker->velocity.x = 0;
24126         attacker->velocity.z = 0;
24127         target->velocity.x = 0;
24128         target->velocity.z = 0;
24129 
24130         /* Check for grab animation, otherwise use orginal throwing system. */
24131         if(validanim(self, ANI_GRAB))
24132         {
24133             if(attacker->model->grabflip & 2)
24134             {
24135                 target->direction = !attacker->direction;
24136             }
24137             attacker->attacking = 0;
24138             memset(attacker->combostep, 0, 5 * sizeof(*attacker->combostep));
24139             target->stalltime = time + GRAB_STALL;
24140             attacker->releasetime = time + (GAME_SPEED / 2);
24141             target->takeaction = common_grabbed;
24142             attacker->takeaction = common_grab;
24143             ent_set_anim(attacker, ANI_GRAB, 0);
24144             set_pain(target, -1, 0); //set grabbed animation
24145         }
24146         else
24147         {
24148             /*
24149             If no throwframewait use original throw code immediately.
24150             Otherwise use throwframewait.
24151             */
24152             if(attacker->modeldata.throwframewait == -1)
24153             {
24154                 dothrow();
24155             }
24156             else
24157             {
24158                 if(self->model->grabflip & 2)
24159                 {
24160                     target->direction = !attacker->direction;
24161                 }
24162 
24163                 target->takeaction = common_prethrow;
24164                 attacker->takeaction = common_throw_wait;
24165                 ent_set_anim(attacker, ANI_THROW, 0);
24166                 set_pain(target, -1, 0); // set grabbed animation
24167             }
24168         }
24169 
24170         result = 1;
24171     }
24172 
24173     return result;
24174 }
24175 
trygrab(entity * other)24176 int trygrab(entity *other)
24177 {
24178     /*
24179     Actions after grab possibility check moved to dograb.
24180     Damon V. Caskey
24181     2013-12-30
24182 
24183     other: Grab target.
24184     */
24185 
24186     int result = 0; //return value.
24187 
24188     if(cangrab(self, other))
24189     {
24190         result = dograb(self, other, DOGRAB_ADJUSTCHECK_TRUE);
24191     }
24192 
24193     return result;
24194 }
24195 
24196 
common_trymove(float xdir,float zdir)24197 int common_trymove(float xdir, float zdir)
24198 {
24199     entity *other = NULL, *te = NULL;
24200     int wall, heightvar, t, needcheckhole = 0;
24201     float x, z, oxdir, ozdir;
24202 
24203     if(!xdir && !zdir)
24204     {
24205         return 0;
24206     }
24207 
24208     // UT: move player grab logic here to bypass some extra checks
24209     // it used to be at the very end of this function
24210     //------------------ grab/throw checking ------------------
24211 
24212     //printf("rand(): %d rand32(): %d\n",rand(),rand32());
24213     // old rand()
24214     if((self->modeldata.type & TYPE_PLAYER) &&
24215             (rand32() & 7) == 0 &&
24216             (validanim(self, ANI_THROW) ||
24217              validanim(self, ANI_GRAB)) && self->idling &&
24218             (other = find_ent_here(self, self->position.x, self->position.z, self->modeldata.hostile, NULL)))
24219     {
24220         if(trygrab(other))
24221         {
24222             return 0;
24223         }
24224     }
24225     // ---------------  end of grab/throw checking ------------------------
24226 
24227 
24228     oxdir = xdir;
24229     ozdir = zdir;
24230     /*
24231     // entity is grabbed by other
24232     if(self->link && self->link->grabbing==self && self->link->grabwalking)
24233     {
24234     	return 1; // just return so we don't have to check twice
24235     }*/
24236 
24237     x = self->position.x + xdir;
24238     z = self->position.z + zdir;
24239     // -----------bounds checking---------------
24240     // Subjec to Z and out of bounds? Return to level!
24241     if (self->modeldata.subject_to_minz > 0)
24242     {
24243         if(z < PLAYER_MIN_Z)
24244         {
24245             zdir = PLAYER_MIN_Z - self->position.z;
24246             execute_onblockz_script(self);
24247         }
24248     }
24249 
24250     if (self->modeldata.subject_to_maxz > 0)
24251     {
24252         if(z > PLAYER_MAX_Z)
24253         {
24254             zdir = PLAYER_MAX_Z - self->position.z;
24255             execute_onblockz_script(self);
24256         }
24257     }
24258 
24259     // screen checking
24260     if(self->modeldata.subject_to_screen > 0)
24261     {
24262         if(x < advancex + 10)
24263         {
24264             xdir = advancex + 10 - self->position.x;
24265             execute_onblocks_script(self);  //Screen block event.
24266         }
24267         else if(x > advancex + (videomodes.hRes - 10))
24268         {
24269             xdir = advancex + (videomodes.hRes - 10) - self->position.x;
24270             execute_onblocks_script(self);  //Screen block event.
24271         }
24272     }
24273 
24274     if(!xdir && !zdir)
24275     {
24276         return 0;
24277     }
24278     x = self->position.x + xdir;
24279     z = self->position.z + zdir;
24280 
24281     //-----------end of bounds checking-----------
24282 
24283     //-------------hole checking ---------------------
24284     // Don't walk into a hole or walk off platforms into holes
24285     if( self->modeldata.subject_to_hole > 0 && !inair(self) && !(self->modeldata.type & TYPE_PLAYER) && self->idling &&
24286             (!self->animation->move[self->animpos]->base || self->animation->move[self->animpos]->base < 0) &&
24287             !(self->modeldata.aimove & AIMOVE2_IGNOREHOLES))
24288     {
24289 
24290         needcheckhole = 1;
24291         // checkhole or checkhole_in() ???
24292         if(zdir && checkhole(self->position.x, z) && checkwall(self->position.x, z) < 0 && !check_platform_below(self->position.x, z, self->position.y, self))
24293         {
24294             //int holeind = checkholeindex_in(self->position.x, z, self->position.y);
24295 
24296             zdir = 0;
24297             //execute_inhole_script(self, 2, (double)level->holes[holeind].height, holeind);
24298         }
24299         if(xdir && checkhole(x, self->position.z) && checkwall(x, self->position.z) < 0 && !check_platform_below(x, self->position.z, self->position.y, self))
24300         {
24301             //int holeind = checkholeindex_in(x, self->position.z, self->position.y);
24302 
24303             xdir = 0;
24304             //execute_inhole_script(self, 1, (double)level->holes[holeind].height, holeind);
24305         }
24306     }
24307 
24308     if(!xdir && !zdir)
24309     {
24310         return 0;
24311     }
24312     x = self->position.x + xdir;
24313     z = self->position.z + zdir;
24314     //-----------end of hole checking---------------
24315 
24316     //--------------obstacle checking ------------------
24317     if(self->modeldata.subject_to_obstacle > 0 /*&& !inair(self)*/)
24318     {
24319         int hit = 0;
24320 
24321         //TODO, check once instead of twice
24322         if((other = find_ent_here(self, x, self->position.z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) &&
24323                 (xdir > 0 ? other->position.x > self->position.x : other->position.x < self->position.x) &&
24324                 (!other->animation->platform || !other->animation->platform[other->animpos][7]))
24325         {
24326             xdir    = 0;
24327             if ( self->falling ) hit |= 1;
24328             te = other;
24329             execute_onblocko_script(self, other);
24330         }
24331         if((other = find_ent_here(self, self->position.x, z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) &&
24332                 (zdir > 0 ? other->position.z > self->position.z : other->position.z < self->position.z) &&
24333                 (!other->animation->platform || !other->animation->platform[other->animpos][7]))
24334         {
24335             zdir    = 0;
24336             if ( self->falling ) hit |= 1;
24337             if(te != other) //just in case they are the same obstacle
24338             {
24339                 execute_onblocko_script(self, other);
24340             }
24341         }
24342 
24343         if ( hit && !self->hitwall && validanim(self, ANI_HITOBSTACLE) ) ent_set_anim(self, ANI_HITOBSTACLE, 0);
24344         if ( hit && !self->hitwall ) self->hitwall = 1;
24345     }
24346 
24347     if(!xdir && !zdir)
24348     {
24349         return 0;
24350     }
24351     x = self->position.x + xdir;
24352     z = self->position.z + zdir;
24353 
24354     //-----------end of obstacle checking--------------
24355 
24356     // ---------------- platform checking----------------
24357 
24358     if(self->animation->size.y)
24359     {
24360         heightvar = self->animation->size.y;
24361     }
24362     else
24363     {
24364         heightvar = self->modeldata.size.y;
24365     }
24366 
24367     // Check for obstacles with platform code and adjust base accordingly
24368     if(self->modeldata.subject_to_platform > 0 )
24369     {
24370         int hit = 0;
24371 
24372         //if(xdir>0 ? other->position.x>self->position.x : other->position.x<self->position.x) {xdir = 0; }
24373         //if(zdir>0 ? other->position.z>self->position.z : other->position.z<self->position.z) {zdir = 0; }
24374         //temporary fix for thin platforms (i.e, offset is not between left and right side)
24375         // TODO: find the collision position, merge with wall code
24376         if(xdir && (other = check_platform_between(x, self->position.z, self->position.y, self->position.y + heightvar, self))  )
24377         {
24378             xdir = 0;
24379             if ( self->falling ) hit |= 1;
24380             execute_onblockp_script(self, 1, other);
24381         }
24382         if(zdir && (other = check_platform_between(self->position.x, z, self->position.y, self->position.y + heightvar, self))  )
24383         {
24384             zdir = 0;
24385             if ( self->falling ) hit |= 1;
24386             execute_onblockp_script(self, 2, other);
24387         }
24388 
24389         if ( hit && !self->hitwall && validanim(self, ANI_HITPLATFORM) ) ent_set_anim(self, ANI_HITPLATFORM, 0);
24390         if ( hit && !self->hitwall ) self->hitwall = 1;
24391     }
24392 
24393     if(!xdir && !zdir)
24394     {
24395         return 0;
24396     }
24397     x = self->position.x + xdir;
24398     z = self->position.z + zdir;
24399 
24400     //-----------end of platform checking------------------
24401 
24402     // ------------------ wall checking ---------------------
24403     if(self->modeldata.subject_to_wall > 0)
24404     {
24405         int hit = 0;
24406 
24407         if(xdir && (wall = checkwall_below(x, self->position.z, 999999)) >= 0 && level->walls[wall].height > self->position.y)
24408         {
24409             xdir = 0;
24410             if ( self->falling && (self->modeldata.hitwalltype < 0 || (self->modeldata.hitwalltype >= 0 && level->walls[wall].type == self->modeldata.hitwalltype)) ) hit |= 1;
24411             execute_onblockw_script(self, 1, (double)level->walls[wall].height, wall);
24412         }
24413         if(zdir && (wall = checkwall_below(self->position.x, z, 999999)) >= 0 && level->walls[wall].height > self->position.y)
24414         {
24415             zdir = 0;
24416             if ( self->falling && (self->modeldata.hitwalltype < 0 || (self->modeldata.hitwalltype >= 0 && level->walls[wall].type == self->modeldata.hitwalltype)) ) hit |= 1;
24417             execute_onblockw_script(self, 2, (double)level->walls[wall].height, wall);
24418         }
24419 
24420         if ( hit && !self->hitwall && validanim(self, ANI_HITWALL) ) ent_set_anim(self, ANI_HITWALL, 0);
24421         if ( hit && !self->hitwall ) self->hitwall = 1;
24422     }
24423 
24424     if(!xdir && !zdir)
24425     {
24426         return 0;
24427     }
24428     x = self->position.x + xdir;
24429     z = self->position.z + zdir;
24430     //----------------end of wall checking--------------
24431 
24432     /*
24433     // Final check to ensure we don't move into other obstacles.
24434     // The old logic allows xdir if zdir is block and vice versa,
24435     // but has a risk that the new destination is actually blocked
24436     // because of multiple obstacle types.
24437     //
24438     // block  old
24439     //    |  /
24440     //    | /
24441     //    |/__ new
24442     */
24443     //xdir = zdir = 0;
24444     // TODO: should we add some checks in testmove to execute those onblockwhatever scripts?
24445     t = testmove(self, self->position.x, self->position.z, x, z);
24446     // extra hole check, only avoid hole while idling
24447     if(t <= 0 && (t != -2 || needcheckhole))
24448     {
24449         return 0;
24450     }
24451 
24452     // do move and return
24453     self->position.x += xdir;
24454     self->position.z += zdir;
24455 
24456     if(xdir)
24457     {
24458         execute_onmovex_script(self);    //X move event.
24459     }
24460     if(zdir)
24461     {
24462         execute_onmovez_script(self);    //Z move event.
24463     }
24464     return 2 - (xdir == oxdir && zdir == ozdir); // return 2 for some checks
24465 }
24466 
24467 // enemies run off after attack
common_runoff()24468 void common_runoff()
24469 {
24470     entity *target = normal_find_target(-1, 0);
24471 
24472     if(target == NULL)   //sealth checking
24473     {
24474         self->velocity.z = self->velocity.x = 0;
24475         self->takeaction = NULL; // OK, back to A.I. root
24476         set_idle(self);
24477         return;
24478     }
24479 
24480     if(!self->modeldata.noflip)
24481     {
24482         self->direction = (self->position.x < target->position.x);
24483     }
24484     if(self->direction == DIRECTION_RIGHT)
24485     {
24486         self->velocity.x = -self->modeldata.speed / 2;
24487     }
24488     else
24489     {
24490         self->velocity.x = self->modeldata.speed / 2;
24491     }
24492 
24493     self->velocity.z = 0;
24494 
24495     if(time > self->stalltime)
24496     {
24497         self->takeaction = NULL;    // OK, back to A.I. root
24498     }
24499 
24500     adjust_walk_animation(target);
24501 }
24502 
24503 
common_stuck_underneath()24504 void common_stuck_underneath()
24505 {
24506     float heightvar = self->animation->size.y ? self->animation->size.y : self->modeldata.size.y;
24507     if(!check_platform_between(self->position.x, self->position.z, self->position.y, self->position.y + heightvar, self) )
24508     {
24509         self->takeaction = NULL;
24510         set_idle(self);
24511         return;
24512     }
24513     if(player[self->playerindex].keys & FLAG_MOVELEFT)
24514     {
24515         self->direction = DIRECTION_LEFT;
24516     }
24517     else if(player[self->playerindex].keys & FLAG_MOVERIGHT)
24518     {
24519         self->direction = DIRECTION_RIGHT;
24520     }
24521     if(player[self->playerindex].playkeys & FLAG_ATTACK && validanim(self, ANI_DUCKATTACK))
24522     {
24523         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
24524         self->takeaction = common_attack_proc;
24525         set_attacking(self);
24526         self->velocity.x = self->velocity.z = 0;
24527         self->combostep[0] = 0;
24528         self->running = 0;
24529         ent_set_anim(self, ANI_DUCKATTACK, 0);
24530         return;
24531     }
24532     if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (player[self->playerindex].playkeys & FLAG_JUMP) && validanim(self, ANI_SLIDE))
24533     {
24534         player[self->playerindex].playkeys &= ~FLAG_JUMP;
24535         self->takeaction = common_attack_proc;
24536         set_attacking(self);
24537         self->velocity.x = self->velocity.z = 0;
24538         self->combostep[0] = 0;
24539         self->running = 0;
24540         ent_set_anim(self, ANI_SLIDE, 0);
24541         return;
24542     }
24543 }
24544 
24545 
24546 // finish attacking, do something
common_attack_finish()24547 void common_attack_finish()
24548 {
24549     entity *target;
24550     int stall;
24551 
24552     self->velocity.x = self->velocity.z = 0;
24553 
24554     if(self->modeldata.type & TYPE_PLAYER)
24555     {
24556         self->takeaction = NULL;
24557         set_idle(self);
24558         return;
24559     }
24560 
24561     target = self->opponent;
24562 
24563     if(target && !self->modeldata.nomove && diff(self->position.x, target->position.x) < 80 && (rand32() & 3))
24564     {
24565         self->takeaction = NULL;//common_runoff;
24566         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);
24567         self->destz = self->position.z;
24568         self->velocity.x = self->position.x > target->position.x ? self->modeldata.speed : -self->modeldata.speed;
24569         self->velocity.z = 0;
24570         adjust_walk_animation(target);
24571         self->idling = 1;
24572     }
24573     else
24574     {
24575         self->takeaction = NULL;
24576         set_idle(self);
24577     }
24578 
24579     stall = GAME_SPEED - self->modeldata.aggression;
24580     if (stall < GAME_SPEED / 2)
24581     {
24582         stall = GAME_SPEED / 2;
24583     }
24584     self->stalltime = time + MAX(0, stall);
24585 }
24586 
24587 //while playing any simple animation
common_animation_normal()24588 void common_animation_normal()
24589 {
24590     if(self->animating)
24591     {
24592         return;
24593     }
24594 
24595     self->takeaction = NULL;
24596     set_idle(self);
24597 
24598     return;
24599 }
24600 
24601 //while playing attack animation
common_attack_proc()24602 void common_attack_proc()
24603 {
24604     if(self->animating || diff(self->position.y, self->base) >= 4)
24605     {
24606         return;
24607     }
24608 
24609     if(self->tocost)
24610     {
24611         if(self->animation->energycost)
24612         {
24613             // Enemy was hit with a special so go ahead and subtract life
24614             if(check_energy(COST_CHECK_MP, self->animnum))
24615             {
24616                 self->mp -= self->animation->energycost->cost;
24617             }
24618             else
24619             {
24620                 self->health -= self->animation->energycost->cost;
24621             }
24622         }
24623         self->tocost = 0;    // Life is subtracted, so go ahead and reset the flag
24624     }
24625 
24626     if(self == smartbomber)
24627     {
24628         // Player is done with the special animation, so unfreeze and execute a smart bomb
24629         smart_bomb(self, self->modeldata.smartbomb);
24630         smartbomber = NULL;
24631     }
24632     if(self->reactive == 1)
24633     {
24634         subtract_shot();
24635         self->reactive = 0;
24636     }
24637     self->attacking = 0;
24638     // end of attack proc
24639     common_attack_finish();
24640 }
24641 
24642 
24643 // dispatch A.I. attack
common_attack()24644 int common_attack()
24645 {
24646     int aiattack ;
24647 
24648     //if(stalker==self) return 0;
24649 
24650     if(time / THINK_SPEED % 4 == 0)
24651     {
24652         return 0;
24653     }
24654 
24655     if(self->modeldata.aiattack == -1)
24656     {
24657         return 0;
24658     }
24659 
24660     aiattack = self->modeldata.aiattack & MASK_AIATTACK1;
24661 
24662     switch(aiattack)
24663     {
24664     case AIATTACK1_LONG:
24665     case AIATTACK1_MELEE:
24666     case AIATTACK1_NOATTACK:
24667         return 0;
24668     default:                    // this is the only available attack style by now
24669         return inair(self) ? 0 : normal_attack();
24670     }
24671 }
24672 
24673 //maybe used many times, so make a function
24674 // A.I. characters will check if there's a wall infront, and jump onto it if possible
24675 // return 1 if jump
common_try_jump()24676 int common_try_jump()
24677 {
24678     float xdir, zdir;
24679     int wall, j = 0;
24680     float rmin, rmax;
24681 
24682     if(validanim(self, ANI_JUMP)) //Can jump?
24683     {
24684         //Check to see if there is a wall within jumping distance and within a jumping height
24685         xdir = 0;
24686         wall = -1;
24687         rmin = (float)self->modeldata.animation[ANI_JUMP]->range.min.x;
24688         rmax = (float)self->modeldata.animation[ANI_JUMP]->range.max.x;
24689         if(self->direction == DIRECTION_RIGHT)
24690         {
24691             xdir = self->position.x + rmin;
24692         }
24693         else
24694         {
24695             xdir = self->position.x - rmin;
24696         }
24697         //check z jump
24698         if(self->modeldata.jumpmovez)
24699         {
24700             zdir = self->position.z + self->velocity.z;
24701         }
24702         else
24703         {
24704             zdir = self->position.z;
24705         }
24706 
24707         if( (wall = checkwall_below(xdir, zdir, 999999)) >= 0 &&
24708                 level->walls[wall].height <= self->position.y + rmax &&
24709                 !inair(self) && self->position.y < level->walls[wall].height  )
24710         {
24711             j = 1;
24712         }
24713         else if(checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) &&
24714                 checkwall(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) < 0 &&
24715                 check_platform (self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir, self) == NULL &&
24716                 !checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? rmax : -rmax), zdir))
24717         {
24718             j = 1;
24719         }
24720     }
24721 
24722     /*
24723     Damon V. Caskey
24724     03292010
24725     AI can will check its RUNJUMP range if JUMP can't reach. Code is pretty redundant,
24726     can probably be moved to a function later.
24727     */
24728     if(!j && validanim(self, ANI_RUNJUMP))														//Jump check failed and can run jump?
24729     {
24730         //Check for wall in range of RUNJUMP.
24731         xdir = 0;
24732         wall = -1;
24733         rmin = (float)self->modeldata.animation[ANI_RUNJUMP]->range.min.x;
24734         rmax = (float)self->modeldata.animation[ANI_RUNJUMP]->range.max.x;
24735         if(self->direction == DIRECTION_RIGHT)
24736         {
24737             xdir = self->position.x + rmin;
24738         }
24739         else
24740         {
24741             xdir = self->position.x - rmin;
24742         }
24743         //check z jump
24744         if(self->modeldata.jumpmovez)
24745         {
24746             zdir = self->position.z + self->velocity.z;
24747         }
24748         else
24749         {
24750             zdir = self->position.z;
24751         }
24752 
24753         if( (wall = checkwall_below(xdir, zdir, 999999)) >= 0 &&
24754                 level->walls[wall].height <= self->position.y + rmax &&
24755                 !inair(self) && self->position.y < level->walls[wall].height  )
24756         {
24757             j = 2;																				//Set to perform runjump.
24758         }
24759         //Check for pit in range of RUNJUMP.
24760         else if(checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) &&
24761                 checkwall(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) < 0 &&
24762                 check_platform (self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir, self) == NULL &&
24763                 !checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? rmax : -rmax), zdir))
24764         {
24765             j = 2;																				//Set to perform runjump.
24766         }
24767     }
24768 
24769     if(j)
24770     {
24771         if(self->running || j == 2)
24772         {
24773             if(validanim(self, ANI_RUNJUMP))														//Running or only within range of RUNJUMP?
24774             {
24775                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_RUNJUMP);
24776             }
24777             else if(validanim(self, ANI_FORWARDJUMP))
24778             {
24779                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
24780             }
24781             else
24782             {
24783                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
24784             }
24785         }
24786         else
24787         {
24788             if(validanim(self, ANI_FORWARDJUMP))
24789             {
24790                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
24791             }
24792             else
24793             {
24794                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
24795             }
24796         }
24797 
24798         return 1;
24799     }
24800     return 0;
24801 }
24802 
24803 //test if direction is available for anim_up
testup(float xd,float zd)24804 static int testup(float xd, float zd)
24805 {
24806     float f;
24807     if(zd < 0)
24808     {
24809         if(!xd)
24810         {
24811             return 1;
24812         }
24813         f = zd / xd;
24814         return (f > 0.5 || f < -0.5);
24815     }
24816     return 0;
24817 }
24818 
24819 //test if direction is available for anim_down
testdown(float xd,float zd)24820 static int testdown(float xd, float zd)
24821 {
24822     float f;
24823     if(zd > 0)
24824     {
24825         if(!xd)
24826         {
24827             return 1;
24828         }
24829         f = zd / xd;
24830         return (f > 0.5 || f < -0.5);
24831     }
24832     return 0;
24833 }
24834 
adjust_walk_animation(entity * other)24835 void adjust_walk_animation(entity *other)
24836 {
24837     int dir = 0;
24838     if(self->running)
24839     {
24840         if (validanim(self, ANI_BACKRUN))
24841         {
24842             if(is_in_backrun(self)) ent_set_anim(self, ANI_BACKRUN, 0);
24843             else ent_set_anim(self, ANI_RUN, 0);
24844         }
24845         else ent_set_anim(self, ANI_RUN, 0); // Set to run animation if exists
24846         return;
24847     }
24848 
24849     //reset the walk animation
24850     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))))
24851     {
24852         common_up_anim(self); //ent_set_anim(self, ANI_UP, 0);
24853         dir = 2;
24854     }
24855     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))))
24856     {
24857         common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0);
24858         dir = 3;
24859     }
24860     else if((self->direction == DIRECTION_RIGHT ? self->velocity.x < 0 : self->velocity.x > 0) && validanim(self, ANI_BACKWALK))
24861     {
24862         common_backwalk_anim(self);    //ent_set_anim(self, ANI_BACKWALK, 0);
24863     }
24864     else
24865     {
24866         common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0);
24867         dir = 1;
24868     }
24869 
24870     if(((self->direction == DIRECTION_RIGHT ? self->velocity.x < 0 : self->velocity.x > 0) && dir == 1) ||
24871             (dir == 2 && self->velocity.z > 0) || (dir == 3 && self->velocity.z < 0) )
24872     {
24873         self->animating = -1;
24874     }
24875     else
24876     {
24877         self->animating = 1;
24878     }
24879 }
24880 
24881 
24882 // ai character try to move towards the item
24883 // TODO, check path or entity might get stuck under a wall
common_try_pick(entity * other)24884 int common_try_pick(entity *other)
24885 {
24886     // if there's an item to pick up, move towards it.
24887     float maxspeed = self->modeldata.speed * 1.5;
24888     float dx = diff(self->position.x, other->position.x);
24889     float dz = diff(self->position.z, other->position.z);
24890 
24891     if(other == NULL || self->modeldata.nomove)
24892     {
24893         return 0;
24894     }
24895 
24896     if(!dz && !dx)
24897     {
24898         self->velocity.x = self->velocity.z = 0;
24899         self->destz = self->position.z;
24900         self->destx = self->position.x;
24901     }
24902     else
24903     {
24904         self->velocity.x = maxspeed * dx / (dx + dz);
24905         self->velocity.z = maxspeed * dz / (dx + dz);
24906         self->destx = other->position.x;
24907         self->destz = other->position.z;
24908     }
24909     if(self->position.x > other->position.x)
24910     {
24911         self->velocity.x = -self->velocity.x;
24912     }
24913     if(self->position.z > other->position.z)
24914     {
24915         self->velocity.z = -self->velocity.z;
24916     }
24917 
24918     self->running = 0;
24919 
24920     adjust_walk_animation(other);
24921 
24922     return 1;
24923 }
24924 
24925 #define astarw 640
24926 #define astarh 360
24927 #define starts (astarw*astarh)
24928 
24929 // not so completed pathfinding logic based on a*
24930 // it should be fairly slow due to the complicacy of terrain checking
24931 // and it doesn't always work since walking from wall to wall
24932 // requires jump.
astar(entity * ent,float destx,float destz,float step,s_axis_f ** wp)24933 int astar(entity *ent, float destx, float destz, float step, s_axis_f **wp)
24934 {
24935     int (*came_from)[astarw][astarh][2] = malloc(sizeof(*came_from));
24936     unsigned char (*closed)[astarw][astarh] = malloc(sizeof(*closed));
24937     int (*openset)[starts][2] = malloc(sizeof(*openset));
24938     float (*gscore)[astarw][astarh] = malloc(sizeof(*gscore));
24939     float (*hscore)[astarw][astarh] = malloc(sizeof(*hscore));
24940     float (*fscore)[astarw][astarh] = malloc(sizeof(*fscore));
24941     int opensize = 0;
24942     int result = 0, mi = 0;
24943     float tg, minf;
24944     int x, z, i, j, tx, tz, better;
24945     static int vx[8] = {0, 1, 1, 1, 0, -1, -1, -1}, vz[8] = { -1, -1, 0, 1, 1, 1, 0, -1};
24946     static float score[8] = {1.0, 1.4, 1.0, 1.4, 1.0, 1.4, 1.0, 1.4};
24947 
24948     int sx = astarw / 2, sz = astarh / 2;
24949     int dx = sx + (destx - ent->position.x) / step, dz = sz + (destz - ent->position.z) / step;
24950 
24951     *wp = NULL;
24952     if(dx < 0 || dx >= astarw || dz < 0 || dz >= astarh)
24953     {
24954         goto pfclearup;
24955     }
24956     memset(closed, 0, sizeof(*closed));
24957     (*openset)[opensize][0] = sx;
24958     (*openset)[opensize][1] = sz;
24959     opensize++;
24960     memset(came_from, 0, sizeof(*came_from));
24961 
24962     (*gscore)[sx][sz] = 0;
24963     (*hscore)[sx][sz] = diff(dx, sx) + diff(dz, sz);
24964     (*fscore)[sx][sz] = (*gscore)[sx][sz] + (*hscore)[sx][sz];
24965     (*came_from)[sx][sz][0] = -1;
24966 
24967     while(opensize > 0)
24968     {
24969         minf = 9999999;
24970         for(j = 0; j < opensize; j++)
24971         {
24972             x = (*openset)[j][0];
24973             z = (*openset)[j][1];
24974             if((*fscore)[x][z] < minf)
24975             {
24976                 minf = (*fscore)[x][z];
24977                 mi = j;
24978             }
24979         }
24980 
24981         x = (*openset)[mi][0];
24982         z = (*openset)[mi][1];
24983         if(x == dx && z == dz)
24984         {
24985             do
24986             {
24987                 tx = (*came_from)[x][z][0];
24988                 tz = (*came_from)[x][z][1];
24989                 result++;
24990                 x = tx;
24991                 z = tz;
24992             }
24993             while(x >= 0);
24994             *wp = malloc(sizeof(*wp) * result);
24995             tx = (*came_from)[dx][dz][0];
24996             tz = (*came_from)[dx][dz][1];
24997             j = 0;
24998             while(tx >= 0)
24999             {
25000                 (*wp)[j].x = (tx - sx) * step + ent->position.x;
25001                 (*wp)[j].z = (tz - sz) * step + ent->position.z;
25002                 x = (*came_from)[tx][tz][0];
25003                 z = (*came_from)[tx][tz][1];
25004                 tx = x;
25005                 tz = z;
25006                 j++;
25007             }
25008             goto pfclearup;
25009         }
25010 
25011         (*openset)[mi][0] = (*openset)[opensize - 1][0];
25012         (*openset)[mi][1] = (*openset)[opensize - 1][1];
25013 
25014         opensize--;
25015         (*closed)[x][z] = 1;
25016 
25017         for(i = 0; i < 8; i++)
25018         {
25019             tx = x + vx[i];
25020             tz = z + vz[i];
25021 
25022             if(tx < 0 || tx >= astarw || tz < 0 || tz >= astarh)
25023             {
25024                 continue;
25025             }
25026             if((*closed)[tx][tz])
25027             {
25028                 continue;
25029             }
25030 
25031             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))
25032             {
25033                 // (*closed)[tx][tz] = 1; // don't add that to close list just in case the entity can jump
25034                 continue;
25035             }
25036 
25037             tg = (*gscore)[x][z] + score[i];
25038 
25039             for(j = 0; j < opensize; j++)
25040             {
25041                 if((*openset)[j][0] == tx && (*openset)[j][1] == tz)
25042                 {
25043                     break;
25044                 }
25045             }
25046 
25047             if(j == opensize)
25048             {
25049                 (*openset)[opensize][0] = tx;
25050                 (*openset)[opensize][1] = tz;
25051                 opensize++;
25052                 better = 1;
25053             }
25054             else if (tg < (*gscore)[tx][tz])
25055             {
25056                 better = 1;
25057             }
25058             else
25059             {
25060                 better = 0;
25061             }
25062 
25063             if(better)
25064             {
25065                 (*came_from)[tx][tz][0] = x;
25066                 (*came_from)[tx][tz][1] = z;
25067                 (*gscore)[tx][tz] = tg;
25068                 (*hscore)[tx][tz] = diff(tx, dx) + diff(tz, dz);
25069                 (*fscore)[tx][tz] = (*gscore)[tx][tz] + (*hscore)[tx][tz];
25070             }
25071         }
25072     }
25073 
25074 pfclearup:
25075     if(came_from)
25076     {
25077         free(came_from);
25078     }
25079     came_from = NULL;
25080     if(closed)
25081     {
25082         free(closed);
25083     }
25084     closed = NULL;
25085     if(openset)
25086     {
25087         free(openset);
25088     }
25089     openset = NULL;
25090     if(gscore)
25091     {
25092         free(gscore);
25093     }
25094     gscore = NULL;
25095     if(hscore)
25096     {
25097         free(hscore);
25098     }
25099     hscore = NULL;
25100     if(fscore)
25101     {
25102         free(fscore);
25103     }
25104     fscore = NULL;
25105 
25106     return result;
25107 }
25108 
25109 
25110 // use this after a wall checking meets
25111 // wall sliding code
25112 // whichside:
25113 //      0
25114 //  1       3
25115 //      2
adjustdirection(float coords[],float offx,float offz,float ox,float oz,float xdir,float zdir,float * cxdir,float * czdir)25116 int adjustdirection(float coords[], float offx, float offz, float ox, float oz, float xdir, float zdir, float *cxdir, float *czdir)
25117 {
25118     float x[4], z[4];
25119     int whichside, i;
25120     float a;
25121 
25122     for(i = 0; i < 4; i++)
25123     {
25124         x[i] = coords[2 + i] + coords[0] + offx;
25125     }
25126     z[1] = z[3] = coords[1] + offz;
25127     z[0] = z[2] = z[1] - coords[6];
25128 
25129     if(oz <= z[0])
25130     {
25131         whichside = 0;
25132     }
25133     else if(oz >= z[1])
25134     {
25135         whichside = 2;
25136     }
25137     else if(ox < x[2])
25138     {
25139         whichside = 1;
25140     }
25141     else
25142     {
25143         whichside = 3;
25144     }
25145 
25146     if(whichside == 0 || whichside == 2)
25147     {
25148         *cxdir = xdir;
25149         *czdir = 0;
25150     }
25151     else
25152     {
25153         if((x[0] == x[1] && whichside == 1) || (x[2] == x[3] && whichside == 3))
25154         {
25155             *cxdir = 0;
25156             *czdir = zdir;
25157         }
25158         else
25159         {
25160             if(whichside == 1)
25161             {
25162                 a = (z[1] - z[0]) / (x[1] - x[0]);
25163             }
25164             else
25165             {
25166                 a = (z[3] - z[2]) / (x[3] - x[2]);
25167             }
25168 
25169             *cxdir = xdir + zdir / a;
25170             *czdir = a * xdir + zdir;
25171 
25172             a = (ABS(xdir) + ABS(zdir)) / (ABS(*cxdir) + ABS(*czdir)) ;
25173             *cxdir *= a;
25174             *czdir *= a;
25175         }
25176     }
25177     //printf("%f, %f, %f, %f, %d\n", xdir, zdir, *cxdir, *czdir, whichside);
25178     return whichside;
25179 }
25180 
25181 // adjust walk speed for entity assuming it walks straight forward
25182 // x, z - current position
25183 // tx, tz - target position
25184 // speed - max speed
25185 // xdir, zdir - return values
adjustspeed(float speed,float x,float z,float tx,float tz,float * xdir,float * zdir)25186 void adjustspeed(float speed, float x, float z, float tx, float tz, float *xdir, float *zdir)
25187 {
25188     float xd, zd;
25189     float dx = diff(tx, x);
25190     float dz = diff(tz, z) * 2;
25191 
25192     if(dx > dz)
25193     {
25194         xd = speed;
25195         zd = xd / dx * dz;
25196     }
25197     else if(dz > dx)
25198     {
25199         zd = speed;
25200         xd = zd / dz * dx;
25201     }
25202     else if(dx)
25203     {
25204         xd = zd = speed;
25205     }
25206     else
25207     {
25208         xd = zd = 0;
25209     }
25210 
25211     if(tx < x)
25212     {
25213         xd = -xd;
25214     }
25215     if(tz < z)
25216     {
25217         zd = -zd;
25218     }
25219 
25220     zd /= 2;
25221 
25222     *xdir = xd;
25223     *zdir = zd;
25224 
25225 }
25226 
checkpathblocked()25227 int checkpathblocked()
25228 {
25229     float x, z, r;
25230     int aitype, wpc;
25231     entity *target;
25232     s_axis_f *wp;
25233     if(self->modeldata.nomove)
25234     {
25235         return 0;
25236     }
25237     if(self->stalltime >= time)
25238     {
25239         aitype = self->modeldata.aimove;
25240         if(self->modeldata.subtype == SUBTYPE_CHASE)
25241         {
25242             aitype |= AIMOVE1_CHASE;
25243         }
25244 
25245         //be moo tolerable to PLAYER_MAX_Z and PLAYER_MIN_Z
25246         if((self->modeldata.subject_to_maxz && self->velocity.z > 0 && !self->velocity.x && self->velocity.z + self->position.z > PLAYER_MAX_Z) ||
25247                 (self->modeldata.subject_to_minz && self->velocity.z < 0 && !self->velocity.x && self->velocity.z + self->position.z < PLAYER_MIN_Z) )
25248         {
25249             self->velocity.z = -self->velocity.z;
25250             self->pathblocked = 0;
25251             self->destz = self->velocity.z > 0 ? (PLAYER_MIN_Z + videomodes.vRes / 10) : (PLAYER_MAX_Z - videomodes.vRes / 10);
25252             adjust_walk_animation(NULL);
25253             return 1;
25254         }
25255 
25256         if(self->pathblocked > 40 || (self->pathblocked > 20 && (aitype & (AIMOVE1_CHASEX | AIMOVE1_CHASEZ | AIMOVE1_CHASE))))
25257         {
25258             if(self->modeldata.pathfindstep > 0)
25259             {
25260                 target = normal_find_target(-1, 0);
25261 
25262                 if(target)
25263                 {
25264                     //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));
25265                     if((wpc = astar(self, target->position.x, target->position.z, self->modeldata.pathfindstep, &wp)) > 0)
25266                     {
25267                         //printf("wp %d\n", wp);
25268                         self->numwaypoints = wpc;
25269                         if(self->waypoints)
25270                         {
25271                             free(self->waypoints);
25272                         }
25273                         self->waypoints = wp;
25274                         self->destx = self->waypoints[self->numwaypoints - 1].x;
25275                         self->destz = self->waypoints[self->numwaypoints - 1].z;
25276                         self->numwaypoints--;
25277                         self->pathblocked = 0;
25278                         return 1;
25279                     }
25280                 }
25281             }
25282 
25283             x = self->velocity.x;
25284             z = self->velocity.z;
25285             if(x > 0)
25286             {
25287                 x = self->modeldata.speed;
25288             }
25289             else if(x < 0)
25290             {
25291                 x = -self->modeldata.speed;
25292             }
25293             if(z > 0)
25294             {
25295                 z = self->modeldata.speed / 2;
25296             }
25297             else if(z < 0)
25298             {
25299                 z = -self->modeldata.speed / 2;
25300             }
25301             r = randf(1);
25302             if(r > 0.6f)
25303             {
25304                 self->velocity.z = x;
25305                 self->velocity.x = -z;
25306             }
25307             else if(r > 0.2f)
25308             {
25309                 self->velocity.z = -x;
25310                 self->velocity.x = z;
25311             }
25312             else
25313             {
25314                 self->velocity.z = (1.0f - randf(2)) * self->modeldata.speed / 2;
25315                 self->velocity.x = (1.0f - randf(2)) * self->modeldata.speed;
25316             }
25317             self->running = 0; // TODO: re-adjust walk speed
25318             self->stalltime = time + GAME_SPEED / 2;
25319             adjust_walk_animation(NULL);
25320             self->pathblocked = 0;
25321 
25322             if(self->velocity.z > 0)
25323             {
25324                 self->destz = self->position.z + 40;
25325             }
25326             else if(self->velocity.z < 0)
25327             {
25328                 self->destz = self->position.z - 40;
25329             }
25330             else
25331             {
25332                 self->destz = self->position.z;
25333             }
25334             if(self->velocity.x > 0)
25335             {
25336                 self->destx = self->position.x + 40;
25337             }
25338             else if(self->velocity.x < 0)
25339             {
25340                 self->destx = self->position.x - 40;
25341             }
25342             else
25343             {
25344                 self->destx = self->position.x;
25345             }
25346 
25347             return 1;
25348 
25349         }
25350     }
25351     return 0;
25352 }
25353 
25354 
25355 // this is the most aggressive aimove pattern
25356 // the entity will try get in and attack at anytime
25357 // though the range depends on what attack you setup
common_try_chase(entity * target,int dox,int doz)25358 int common_try_chase(entity *target, int dox, int doz)
25359 {
25360     // start chasing the target
25361     float dx, dz, range;
25362     int randomatk;
25363 
25364     self->running = 0;
25365 
25366     //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);
25367 
25368     if(target == NULL || self->modeldata.nomove)
25369     {
25370         return 0;
25371     }
25372 
25373     randomatk = pick_random_attack(NULL, 0);
25374 
25375     if(randomatk >= 0)
25376     {
25377         range = (self->modeldata.animation[randomatk]->range.min.x + self->modeldata.animation[randomatk]->range.max.x) / 2;
25378         //printf("range picked: ani %d, range %f\n", randomatk, range);
25379         if(range < 0)
25380         {
25381             range = self->modeldata.grabdistance;
25382         }
25383         else if(range > videomodes.hRes / 4)
25384         {
25385             range = videomodes.hRes / 4;
25386         }
25387     }
25388     else
25389     {
25390         range = self->modeldata.grabdistance;
25391     }
25392 
25393     if(dox)
25394     {
25395         if(self->position.x > target->position.x)
25396         {
25397             self->destx = target->position.x + range - 1;
25398         }
25399         else
25400         {
25401             self->destx = target->position.x - range + 1;
25402         }
25403         dx = diff(self->position.x, self->destx);
25404 
25405         if(dx > 150 && validanim(self, ANI_RUN))
25406         {
25407             self->velocity.x = self->modeldata.runspeed;
25408             self->running = 1;
25409         }
25410         else
25411         {
25412             self->velocity.x = self->modeldata.speed;
25413         }
25414         if(self->destx < self->position.x)
25415         {
25416             self->velocity.x = -self->velocity.x;
25417         }
25418     }
25419 
25420     if(doz)
25421     {
25422         self->destz = target->position.z ;
25423         dz = diff(self->position.z, self->destz);
25424 
25425         if(dz > 100 && self->modeldata.runupdown && validanim(self, ANI_RUN))
25426         {
25427             self->velocity.z = self->modeldata.runspeed / 2;
25428             self->running = 1;
25429         }
25430         else
25431         {
25432             self->velocity.z = self->modeldata.speed / 2;
25433         }
25434         if(self->destz < self->position.z)
25435         {
25436             self->velocity.z = -self->velocity.z;
25437         }
25438     }
25439 
25440     return 1;
25441 }
25442 
25443 //may be used many times, so make a function
25444 // minion follow his owner
common_try_follow(entity * target,int dox,int doz)25445 int common_try_follow(entity *target, int dox, int doz)
25446 {
25447     // start chasing the target
25448     float dx, dz, distance;
25449     int mx, mz;
25450     int facing;
25451 
25452     //target = self->parent;
25453     if(target == NULL || self->modeldata.nomove)
25454     {
25455         return 0;
25456     }
25457     distance = (float)((validanim(self, ANI_IDLE)) ? self->modeldata.animation[ANI_IDLE]->range.min.x : 100);
25458 
25459     if(distance <= 0)
25460     {
25461         distance = 100.0;
25462     }
25463 
25464     facing = (self->direction == DIRECTION_RIGHT ? self->position.x < target->position.x : self->position.x > target->position.x);
25465 
25466     dx = diff(self->position.x, target->position.x);
25467     dz = diff(self->position.z, target->position.z);
25468 
25469     if(dox && dx < distance)
25470     {
25471         self->velocity.x = 0;
25472         mx = 0;
25473     }
25474     else
25475     {
25476         mx = 1;
25477     }
25478 
25479     if(doz && dz < distance / 2)
25480     {
25481         self->velocity.z = 0;
25482         mz = 0;
25483     }
25484     else
25485     {
25486         mz = 1;
25487     }
25488 
25489     if(dox && mx)
25490     {
25491         if(facing && dx > 200 && validanim(self, ANI_RUN))
25492         {
25493             self->velocity.x = self->modeldata.runspeed;
25494             self->running = 1;
25495         }
25496         else
25497         {
25498             self->velocity.x = self->modeldata.speed;
25499             self->running = 0;
25500         }
25501         if(self->position.x > target->position.x)
25502         {
25503             self->velocity.x = -self->velocity.x;
25504         }
25505         self->destx = target->position.x;
25506     }
25507 
25508     if(doz && mz)
25509     {
25510         if(facing && dx > 200 && self->modeldata.runupdown && validanim(self, ANI_RUN))
25511         {
25512             self->velocity.z = self->modeldata.runspeed / 2;
25513             self->running = 1;
25514         }
25515         else
25516         {
25517             self->velocity.z = self->modeldata.speed / 2;
25518             self->running = 0; // not right, to be modified
25519         }
25520         if(self->position.z > target->position.z)
25521         {
25522             self->velocity.z = -self->velocity.z;
25523         }
25524         self->destz = target->position.z;
25525     }
25526 
25527 
25528     return 1;
25529 }
25530 
25531 // try to avoid the target
25532 // used by 'avoid avoidz avoidx
25533 // 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)25534 int common_try_avoid(entity *target, int dox, int doz)
25535 {
25536     float dx, dz;
25537     float maxdz, mindz, maxdx, mindx;
25538     int randomatk;
25539 
25540     if(target == NULL || self->modeldata.nomove)
25541     {
25542         return 0;
25543     }
25544 
25545     dx = diff(self->position.x, target->position.x);
25546     dz = diff(self->position.z, target->position.z);
25547 
25548     randomatk = pick_random_attack(NULL, 0);
25549 
25550     if((rand32() & 15) < 8 && randomatk >= 0)
25551     {
25552         maxdx = self->modeldata.animation[randomatk]->range.max.x - self->modeldata.speed;
25553         if(maxdx < videomodes.hRes / 5)
25554         {
25555             maxdx = videomodes.hRes / 5;
25556         }
25557         mindx = maxdx - 10;
25558         maxdz = self->modeldata.animation[randomatk]->range.max.z - self->modeldata.speed;
25559         if(maxdz < videomodes.vRes / 5)
25560         {
25561             maxdz = videomodes.vRes / 5;
25562         }
25563         mindz = maxdz - 10;
25564     }
25565     else
25566     {
25567         mindx = videomodes.hRes / 3;
25568         maxdx = videomodes.hRes / 2;
25569         mindz = videomodes.vRes / 3;
25570         maxdz = videomodes.vRes / 2;
25571     }
25572 
25573     if(dox)
25574     {
25575         if(self->position.x < screenx)
25576         {
25577             self->velocity.x = self->modeldata.speed;
25578             self->destx = screenx + videomodes.hRes / 12.0;
25579         }
25580         else if(self->position.x > screenx + videomodes.hRes)
25581         {
25582             self->velocity.x = -self->modeldata.speed;
25583             self->destx = screenx + videomodes.hRes * 11.0 / 12.0;
25584         }
25585         else if(dx < mindx)
25586         {
25587             self->velocity.x = (self->position.x < target->position.x) ? (-self->modeldata.speed) : self->modeldata.speed;
25588             self->destx = (self->position.x < target->position.x) ? (target->position.x - maxdx) : (target->position.x + maxdx);
25589         }
25590         else if (dx > maxdx)
25591         {
25592             self->velocity.x = (self->position.x < target->position.x) ? self->modeldata.speed : (-self->modeldata.speed);
25593             self->destx = (self->position.x < target->position.x) ? (target->position.x - mindx) : (target->position.x + mindx);
25594         }
25595         else
25596         {
25597             self->velocity.x = 0;
25598             self->destx = self->position.x;
25599         }
25600     }
25601 
25602     if(doz)
25603     {
25604         if(self->position.z < screeny)
25605         {
25606             self->velocity.z = self->modeldata.speed / 2;
25607             self->destz = screeny +  videomodes.vRes / 12.0;
25608         }
25609         else if(self->position.z > screeny + videomodes.vRes)
25610         {
25611             self->velocity.z = -self->modeldata.speed / 2;
25612             self->destz = screeny +  videomodes.vRes * 11.0 / 12.0;
25613         }
25614         else if(dz < mindz)
25615         {
25616             self->velocity.z = (self->position.z < target->position.z) ? (-self->modeldata.speed / 2) : (self->modeldata.speed / 2);
25617             self->destz = (self->position.z < target->position.z) ? (target->position.z - maxdz) : (target->position.z + maxdz);
25618         }
25619         else if(dz > maxdz)
25620         {
25621             self->velocity.z = (self->position.z < target->position.z) ? (self->modeldata.speed / 2) : (-self->modeldata.speed / 2);
25622             self->destz = (self->position.z < target->position.z) ? (target->position.z - mindz) : (target->position.z + mindz);
25623         }
25624         else
25625         {
25626             self->velocity.z = 0;
25627             self->destz = self->position.z;
25628         }
25629     }
25630 
25631     return 1;
25632 }
25633 
25634 //  wander completely and ignore the target
25635 // this ai pattern only works when you use aimove wander,
25636 // if you mix wander with other patterns like chase or avoid
25637 // this pattern is not triggered
common_try_wandercompletely(int dox,int doz)25638 int common_try_wandercompletely(int dox, int doz)
25639 {
25640     int rnum;
25641 
25642     if(self->modeldata.nomove)
25643     {
25644         return 0;
25645     }
25646 
25647     if(dox)
25648     {
25649         rnum = rand32() & 15;
25650         if(rnum < 4)
25651         {
25652             self->velocity.x = -self->modeldata.speed;
25653         }
25654         else if(rnum > 11)
25655         {
25656             self->velocity.x = self->modeldata.speed;
25657         }
25658         else
25659         {
25660             self->velocity.x = 0;
25661         }
25662         if( self->position.x < screenx - 10)
25663         {
25664             self->velocity.x = self->modeldata.speed;
25665         }
25666         else if(self->position.x > screenx + videomodes.hRes + 10)
25667         {
25668             self->velocity.x = -self->modeldata.speed;
25669         }
25670 
25671         if(self->velocity.x > 0)
25672         {
25673             self->destx = self->position.x + videomodes.hRes / 5;
25674         }
25675         else if(self->velocity.x < 0)
25676         {
25677             self->destx = self->position.x - videomodes.hRes / 5;
25678         }
25679         else
25680         {
25681             self->destx = self->position.x;
25682         }
25683 
25684     }
25685     if(doz)
25686     {
25687         rnum = rand32() & 15;
25688         if(rnum < 4)
25689         {
25690             self->velocity.z = -self->modeldata.speed / 2;
25691         }
25692         else if(rnum > 11)
25693         {
25694             self->velocity.z = self->modeldata.speed / 2;
25695         }
25696         else
25697         {
25698             self->velocity.z = 0;
25699         }
25700         if(self->position.z < screeny - 10)
25701         {
25702             self->velocity.z = self->modeldata.speed / 2;
25703         }
25704         else if(self->position.z > screeny + videomodes.vRes + 10)
25705         {
25706             self->velocity.z = -self->modeldata.speed / 2;
25707         }
25708 
25709         if(self->velocity.z > 0)
25710         {
25711             self->destz = self->position.z + videomodes.vRes / 5;
25712         }
25713         else if(self->velocity.z < 0)
25714         {
25715             self->destz = self->position.z - videomodes.vRes / 5;
25716         }
25717         else
25718         {
25719             self->destz = self->position.z;
25720         }
25721 
25722     }
25723 
25724     return 1;
25725 
25726 }
25727 /*
25728 int assume_safe_distance(entity* target, int ani, int* minx, int* maxx, int* minz, int* maxz)
25729 {
25730 	int f, set = 0;
25731 	short tminx, tmaxx, tminz, tmaxz;
25732 	s_anim* ta;
25733 	short* coords;
25734 	if(validanim(target, ani)){
25735 		ta = target->modeldata.animation[ani];
25736 		*minx = *minz = 9999;
25737 		*maxx = *maxz = -9999;
25738 		if(ta->collision_attack){
25739 			for(f=0; f<ta->numframes; f++){
25740 				if(!ta->collision_attack[f]) continue;
25741 				coords = ta->collision_attack[f]->coords;
25742 				if(target->direction == DIRECTION_RIGHT) {
25743 					tminx = coords[0];
25744 					tmaxx = coords[2];
25745 				}else{
25746 					tminx = -coords[2];
25747 					tmaxx = -coords[0];
25748 				}
25749 				tminz = -coords[4];
25750 				tmaxz = coords[4];
25751 				if(tminx<*minx)
25752 					*minx = tminx;
25753 				if(tminz<*minz)
25754 					*minz = tminz;
25755 				if(tmaxx>*maxx)
25756 					*maxx = tmaxx;
25757 				if(tmaxz>*maxz)
25758 					*maxz = tmaxz;
25759 
25760 				set = 1;
25761 			}
25762 
25763 			if(set && self->animation->collision_body->coords && self->animation->collision_body[self->animpos]->coords){
25764 				coords = self->animation->collision_body[self->animpos]->coords;
25765 				*minx -= coords[2] - coords[0];
25766 				*minz -= coords[4];
25767 				*maxx += coords[2] - coords[0];
25768 				*maxz += coords[4];
25769 			}
25770 
25771 			return set;
25772 		}
25773 	}
25774 
25775 	return 0;
25776 
25777 }
25778 */
25779 // for normal and default ai patttern
25780 // the entity is not actually wandering
25781 // they just go around the target and get close
25782 // occasionally to attack
common_try_wander(entity * target,int dox,int doz)25783 int common_try_wander(entity *target, int dox, int doz)
25784 {
25785     int walk = 0, behind, grabd, mod;
25786 
25787     float diffx, diffz, //distance from target
25788           returndx, returndz, //how far should keep from target
25789           borderdx, borderdz, //how far should keep offscreen
25790           mindx, mindz;// don't walk into the target
25791     int rnum = rand32() & 15, t, randomatk;
25792 
25793     if(!target || self->modeldata.nomove)
25794     {
25795         return 0;
25796     }
25797 
25798     diffx = diff(self->position.x, target->position.x);
25799     diffz = diff(self->position.z, target->position.z);
25800     behind = ((self->position.x < target->position.x) == target->direction);
25801     grabd = self->modeldata.grabdistance;
25802     //when entity is behind the target, it has a greater chance to go after the target
25803     if(behind && diffx < grabd * 4 && diffz < grabd) //right behind, go for it
25804     {
25805         t = 13;
25806     }
25807     else if(behind)   // only behind, half chance
25808     {
25809         t = 7;
25810     }
25811     else    // otherwise, 1/8 chance
25812     {
25813         t = 2;
25814     }
25815 
25816     if(behind && target->attacking)
25817     {
25818         t += 5;
25819     }
25820 
25821     // could use this to replace the completely wander ai
25822     if(dox != doz)
25823     {
25824         t = 0;
25825     }
25826 
25827     if(rnum < t) //chase target
25828     {
25829         returndx = videomodes.hRes / 4;
25830         returndz = videomodes.vRes / 8;
25831     }
25832     else   // only chase when too far away
25833     {
25834         returndx = videomodes.hRes * 0.6;
25835         returndz = videomodes.vRes * 0.4;
25836     }
25837     if(rnum > 7)
25838     {
25839         borderdx = videomodes.hRes / 8;
25840         borderdz = videomodes.vRes / 8;
25841     }
25842     else
25843     {
25844         borderdx = borderdz = 0;
25845     }
25846 
25847     randomatk = pick_random_attack(NULL, 0);
25848 
25849     if(randomatk >= 0)
25850     {
25851         mindx = self->modeldata.animation[randomatk]->range.max.x - (self->modeldata.animation[randomatk]->range.max.x - self->modeldata.animation[randomatk]->range.min.x) / 4 - 1;
25852     }
25853     else
25854     {
25855         mindx = (!behind && target->attacking) ? grabd * 3 : grabd * 1.2;
25856     }
25857     mindz = grabd / 4;
25858 
25859     //mod = ((int)(time / (videomodes.hRes / self->modeldata.speed)) + self->sortid / 100 + self->health / 3 + self->pathblocked + self->modeldata.aggression / 10) % 4;
25860     mod = ((int)(time / (videomodes.hRes / self->modeldata.speed)) + 1000 + self->health / 3 + self->pathblocked + self->modeldata.aggression / 10) % 4;
25861     if(mod < 0)
25862     {
25863         mod = -mod;
25864     }
25865     //if ((self->sortid / 100) % 2)
25866     if (rand32() % 2)
25867     {
25868         mod = 3 - mod;
25869     }
25870 
25871     if(dox)
25872     {
25873         if(self->position.x < screenx - borderdx)
25874         {
25875             self->velocity.x = self->modeldata.speed;
25876             self->destx = screenx + videomodes.hRes / 8.0;
25877             walk = 1;
25878         }
25879         else if (self->position.x > screenx + videomodes.hRes + borderdx)
25880         {
25881             self->velocity.x = -self->modeldata.speed;
25882             self->destx = screenx + videomodes.hRes * 7.0 / 8.0;
25883             walk = 1;
25884         }
25885         else if(diffx > returndx)
25886         {
25887             self->velocity.x = (self->position.x > target->position.x) ? -self->modeldata.speed : self->modeldata.speed;
25888             self->destx = (self->position.x > target->position.x) ? (target->position.x + mindx) : (target->position.x - mindx);
25889             walk = 1;
25890         }
25891         else
25892         {
25893             switch(mod)
25894             {
25895             case 0:
25896                 self->destx = target->position.x + grabd;
25897                 break;
25898             case 2:
25899                 self->destx = target->position.x - grabd;
25900                 break;
25901             case 1:
25902                 self->destx = target->position.x + videomodes.hRes * 0.4 + (self->health / 3 % 20);
25903                 break;
25904             case 3:
25905                 self->destx = target->position.x - videomodes.hRes * 0.4 - (self->health / 3 % 20);
25906                 break;
25907             }
25908             self->velocity.x = self->position.x > self->destx ? -self->modeldata.speed : self->modeldata.speed;
25909             walk = 1;
25910             //printf("mod x %d\n", mod);
25911         }
25912     }
25913 
25914     if(doz)
25915     {
25916         if(self->position.z < screeny - borderdz)
25917         {
25918             self->velocity.z = self->modeldata.speed / 2;
25919             self->destz = screeny + videomodes.vRes / 12.0;
25920             walk |= 1;
25921         }
25922         else if (self->position.z > screeny + videomodes.vRes + borderdz)
25923         {
25924             self->velocity.z = -self->modeldata.speed / 2;
25925             self->destz = screeny + videomodes.vRes * 11.0 / 12.0;
25926             walk |= 1;
25927         }
25928         else if(diffz > returndz)
25929         {
25930             self->velocity.z = (self->position.z > target->position.z) ? -self->modeldata.speed / 2 : self->modeldata.speed / 2;
25931             self->destz = (self->position.z > target->position.z) ? (target->position.z + mindz) : (target->position.z - mindz);
25932             walk |= 1;
25933         }
25934         else
25935         {
25936             switch(mod)
25937             {
25938             case 1:
25939                 self->destz = target->position.z + grabd / 2;
25940                 break;
25941             case 3:
25942                 self->destz = target->position.z - grabd / 2;
25943                 break;
25944             case 2:
25945                 self->destz = target->position.z + MIN((PLAYER_MAX_Z - PLAYER_MIN_Z), videomodes.vRes) * 0.25 + (self->health / 3 % 5);
25946                 break;
25947             case 0:
25948                 self->destz = target->position.z - MIN((PLAYER_MAX_Z - PLAYER_MIN_Z), videomodes.vRes) * 0.25 - (self->health / 3 % 5);
25949                 break;
25950             }
25951             self->velocity.z = self->position.z > self->destz ? -self->modeldata.speed / 2 : self->modeldata.speed / 2;
25952             walk |= 1;
25953             //printf("mod z %d\n", mod);
25954         }
25955     }
25956 
25957     return walk;
25958 }
25959 
25960 //A.I chracter pickup an item
common_pickupitem(entity * other)25961 void common_pickupitem(entity *other)
25962 {
25963     int pickup = 0;
25964     //weapons
25965     if(self->weapent == NULL && isSubtypeWeapon(other) && validanim(self, ANI_GET))
25966     {
25967         self->takeaction = common_get;
25968         dropweapon(0);  //don't bother dropping the previous one though, scine it won't pickup another
25969         self->weapent = other;
25970         set_weapon(self, other->modeldata.weapnum, 0);
25971         set_getting(self);
25972         self->velocity.x = self->velocity.z = 0; //stop moving
25973         if(self->modeldata.animal)  // UTunnels: well, ride, not get. :)
25974         {
25975             self->direction = other->direction;
25976             self->position.x = other->position.x;
25977             self->position.z = other->position.z;
25978         }
25979         other->nextanim = other->nextthink = time + GAME_SPEED * 999999;
25980         ent_set_anim(self, ANI_GET, 0);
25981         pickup = 1;
25982     }
25983     // projectiles
25984     else if(self->weapent == NULL && isSubtypeProjectile(other) && validanim(self, ANI_GET))
25985     {
25986         self->takeaction = common_get;
25987         dropweapon(0);
25988         self->weapent = other;
25989         set_getting(self);
25990         self->velocity.x = self->velocity.z = 0; //stop moving
25991         other->nextanim = other->nextthink = time + GAME_SPEED * 999999;
25992         ent_set_anim(self, ANI_GET, 0);
25993         pickup = 1;
25994     }
25995     // other items
25996     else if(! isSubtypeWeapon(other) && ! isSubtypeProjectile(other) )
25997     {
25998         if(validanim(self, ANI_GET) && !isSubtypeTouch(other))
25999         {
26000             self->takeaction = common_get;
26001             set_getting(self);
26002             self->velocity.x = self->velocity.z = 0; //stop moving
26003             ent_set_anim(self, ANI_GET, 0);
26004         }
26005         if(other->health)
26006         {
26007             self->health += other->health;
26008             if(self->health > self->modeldata.health)
26009             {
26010                 self->health = self->modeldata.health;
26011             }
26012             other->health = 0;
26013             //if(SAMPLE_GET >= 0) sound_play_sample(SAMPLE_GET, 0, savedata.effectvol,savedata.effectvol, 100);
26014         }
26015         // else if, TODO: other effects
26016         // kill that item
26017         other->takeaction = suicide;
26018         other->nextthink = time + GAME_SPEED * 3;
26019         pickup = 1;
26020     }
26021     // hide it
26022     if(pickup)
26023     {
26024         execute_didhit_script(other, self, 0, 0, other->modeldata.subtype, 0, 0, 0, 0, 0, 0); //Execute didhit script as if item "hit" collecter to allow easy item scripting.
26025         other->position.z = 100000;
26026     }
26027 }
26028 
26029 // for old bikers
biker_move()26030 int biker_move()
26031 {
26032 
26033     if((self->direction == DIRECTION_RIGHT) ? (self->position.x > advancex + (videomodes.hRes + 200)) : (self->position.x < advancex - 200))
26034     {
26035         self->direction = !self->direction;
26036         self->attack_id = 0;
26037         self->position.z = (float)(PLAYER_MIN_Z + randf((float)(PLAYER_MAX_Z - PLAYER_MIN_Z)));
26038         if(SAMPLE_BIKE >= 0)
26039         {
26040             sound_play_sample(SAMPLE_BIKE, 0, savedata.effectvol, savedata.effectvol, 100);
26041         }
26042         if(self->modeldata.speed)
26043         {
26044             self->velocity.x = (self->direction == DIRECTION_RIGHT) ? (self->modeldata.speed) : (-self->modeldata.speed);
26045         }
26046         else
26047         {
26048             self->velocity.x = (self->direction == DIRECTION_RIGHT) ? ((float)1.7 + randf((float)0.6)) : (-((float)1.7 + randf((float)0.6)));
26049         }
26050     }
26051 
26052     return 1;
26053 }
26054 
26055 // for common arrow types
arrow_move()26056 int arrow_move()
26057 {
26058     int wall;
26059     float dx;
26060     float dz;
26061     float maxspeed;
26062     entity *target = NULL;
26063 
26064     // new subtype chase
26065     if(self->modeldata.subtype == SUBTYPE_CHASE)
26066     {
26067         target = homing_find_target(self->modeldata.hostile);
26068 
26069         if(target)
26070         {
26071             if(!self->modeldata.noflip)
26072             {
26073                 self->direction = (target->position.x > self->position.x);
26074             }
26075             // start chasing the target
26076             dx = diff(self->position.x, target->position.x);
26077             dz = diff(self->position.z, target->position.z);
26078             maxspeed = self->modeldata.speed * 1.5;
26079 
26080             if(!dz && !dx)
26081             {
26082                 self->velocity.x = self->velocity.z = 0;
26083             }
26084             else
26085             {
26086                 self->velocity.x = maxspeed * dx / (dx + dz);
26087                 self->velocity.z = maxspeed * dz / (dx + dz);
26088             }
26089             if(self->direction == DIRECTION_LEFT)
26090             {
26091                 self->velocity.x = -self->velocity.x;
26092             }
26093             if(self->position.z > target->position.z)
26094             {
26095                 self->velocity.z = -self->velocity.z;
26096             }
26097         }
26098         else
26099         {
26100             if(!self->velocity.x && !self->velocity.z)
26101             {
26102                 if(self->direction == DIRECTION_LEFT)
26103                 {
26104                     self->velocity.x = -self->modeldata.speed;
26105                 }
26106                 else if(self->direction == DIRECTION_RIGHT)
26107                 {
26108                     self->velocity.x = self->modeldata.speed;
26109                 }
26110             }
26111         }
26112         if(!self->modeldata.nomove)
26113         {
26114             if(target && self->position.z > target->position.z && validanim(self, ANI_UP))
26115             {
26116                 common_up_anim(self);    //ent_set_anim(self, ANI_UP, 0);
26117             }
26118             else if(target && target->position.z > self->position.z && validanim(self, ANI_DOWN))
26119             {
26120                 common_down_anim(self);    //ent_set_anim(self, ANI_DOWN, 0);
26121             }
26122             else if(validanim(self, ANI_WALK))
26123             {
26124                 common_walk_anim(self);    //ent_set_anim(self, ANI_WALK, 0);
26125             }
26126             else
26127             {
26128                 if(validanim(self, ANI_IDLE)) ent_set_anim(self, ANI_IDLE, 0);
26129             }
26130         }
26131     }
26132     else
26133     {
26134         // Now projectiles can have custom speeds
26135         if(self->direction == DIRECTION_LEFT)
26136         {
26137             self->velocity.x = -self->modeldata.speed;
26138         }
26139         else if(self->direction == DIRECTION_RIGHT)
26140         {
26141             self->velocity.x = self->modeldata.speed;
26142         }
26143     }
26144 
26145     if(level)
26146     {
26147         if(self->modeldata.subject_to_wall && (wall = checkwall(self->position.x, self->position.z)) >= 0 && self->position.y < level->walls[wall].height)
26148         {
26149             // Added so projectiles bounce off blocked exits
26150             if(validanim(self, ANI_FALL))
26151             {
26152                 self->takeaction = common_fall;
26153                 self->attacking = 0;
26154                 self->health = 0;
26155                 self->projectile = 0;
26156                 if(self->direction == DIRECTION_LEFT)
26157                 {
26158                     self->velocity.x = (float) - 1.2;
26159                 }
26160                 else if(self->direction == DIRECTION_RIGHT)
26161                 {
26162                     self->velocity.x = (float)1.2;
26163                 }
26164                 self->damage_on_landing = 0;
26165                 toss(self, 2.5 + randf(1));
26166                 self->modeldata.no_adjust_base = 0;
26167                 self->modeldata.subject_to_wall = self->modeldata.subject_to_platform = self->modeldata.subject_to_hole = self->modeldata.subject_to_gravity = 1;
26168                 set_fall(self, ATK_NORMAL, 0, self, 100000, 0, 0, 0, 0, 0, 0);
26169             }
26170         }
26171     }
26172     self->autokill |= self->ptype;
26173     return 1;
26174 }
26175 
26176 // for common boomerang types
boomerang_move()26177 int boomerang_move()
26178 {
26179     if(!self->modeldata.nomove)
26180     {
26181         float acc, distx, current_distx;
26182 
26183         if(self->modeldata.boomerang_acc != 0) acc = self->modeldata.boomerang_acc;
26184         else acc = self->modeldata.speed/(GAME_SPEED/20); // acceleration
26185         if(self->modeldata.boomerang_distx > 0) distx = self->modeldata.boomerang_distx;
26186         else distx = videomodes.hRes/(3); // horizontal distance
26187 
26188         // init
26189         if(self->velocity.x == 0 && !self->boomerang_loop)
26190         {
26191             self->modeldata.noflip = 1;
26192 
26193             if(self->parent)
26194             {
26195                 self->modeldata.hostile &= ~(self->parent->modeldata.type);
26196                 if (self->parent->modeldata.type == TYPE_PLAYER) self->modeldata.hostile |= TYPE_ENEMY;
26197                 else if(self->parent->modeldata.type == TYPE_ENEMY) self->modeldata.hostile |= (TYPE_PLAYER | TYPE_NPC);
26198 
26199                 self->direction = self->parent->direction;
26200                 self->sortid = self->parent->sortid + 1;
26201             }
26202 
26203             if(self->direction == DIRECTION_LEFT)
26204             {
26205                 self->velocity.x = -self->modeldata.speed;
26206             }
26207             else if(self->direction == DIRECTION_RIGHT)
26208             {
26209                 self->velocity.x = self->modeldata.speed;
26210             }
26211             self->position.z = self->parent->position.z;
26212             self->position.y = self->parent->position.y;
26213 
26214             self->modeldata.antigrab = 1;
26215             self->modeldata.grabforce = -999999;
26216 
26217             ++self->boomerang_loop;
26218         }
26219         if(self->velocity.z != 0) self->velocity.z = 0;
26220 
26221         if( (!self->parent && (self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80))) )
26222         {
26223             kill(self);
26224             return 0;
26225         }
26226 
26227         if(self->parent)
26228         {
26229             current_distx = diff(self->position.x,self->parent->position.x);
26230             self->position.z = self->parent->position.z;
26231             self->position.y = self->parent->position.y;
26232 
26233             // moving
26234             if (self->position.x >= self->parent->position.x)
26235             {
26236                 if ( current_distx >= distx )
26237                 {
26238                     if(self->velocity.x - acc <= 0 && self->velocity.x > 0)
26239                     {
26240                         ++self->boomerang_loop;
26241                         if(self->sortid <= self->parent->sortid) self->sortid = self->parent->sortid + 1;
26242                         else self->sortid = self->parent->sortid - 1;
26243                     }
26244                     self->velocity.x = (self->velocity.x-acc < -self->modeldata.speed)?(-self->modeldata.speed):(self->velocity.x-acc);
26245                 }
26246                 else if (self->velocity.x <= 0) self->velocity.x = (self->velocity.x-acc < -self->modeldata.speed)?(-self->modeldata.speed):(self->velocity.x-acc);
26247             }
26248             else if (self->position.x <= self->parent->position.x)
26249             {
26250                 if ( current_distx >= distx )
26251                 {
26252                     if(self->velocity.x + acc >= 0 && self->velocity.x < 0)
26253                     {
26254                         ++self->boomerang_loop;
26255                         if(self->sortid <= self->parent->sortid) self->sortid = self->parent->sortid + 1;
26256                         else self->sortid = self->parent->sortid - 1;
26257                     }
26258                     self->velocity.x = (self->velocity.x+acc > self->modeldata.speed)?(self->modeldata.speed):(self->velocity.x+acc);
26259                 }
26260                 else if (self->velocity.x >= 0) self->velocity.x = (self->velocity.x+acc > self->modeldata.speed)?(self->modeldata.speed):(self->velocity.x+acc);
26261             }
26262 
26263             // grab the boomerang
26264             if( (self->position.x >= self->parent->position.x && self->parent->direction == DIRECTION_RIGHT && self->velocity.x <= 0) ||
26265                 (self->position.x <= self->parent->position.x && self->parent->direction == DIRECTION_LEFT  && self->velocity.x >= 0) )
26266             {
26267                if( !self->parent->inpain && !self->parent->falling && !self->parent->dead && self->boomerang_loop > 1 )
26268                {
26269                     if(self->parent->position.y <= self->parent->base && validanim(self->parent, ANI_GETBOOMERANG))
26270                     {
26271                         if( current_distx >= self->parent->modeldata.animation[ANI_GETBOOMERANG]->range.min.x && current_distx <= self->parent->modeldata.animation[ANI_GETBOOMERANG]->range.max.x )
26272                         {
26273                             self->parent->takeaction = common_animation_normal;
26274                             self->parent->attacking = 0;
26275                             self->parent->idling = 0;
26276                             ent_set_anim(self->parent, ANI_GETBOOMERANG, 0);
26277                             kill(self);
26278                             return 1;
26279                         }
26280                     }
26281                     else if(inair(self->parent) && validanim(self->parent, ANI_GETBOOMERANGINAIR))
26282                     {
26283                         if( current_distx >= self->parent->modeldata.animation[ANI_GETBOOMERANGINAIR]->range.min.x && current_distx <= self->parent->modeldata.animation[ANI_GETBOOMERANGINAIR]->range.max.x )
26284                         {
26285                             self->parent->takeaction = common_animation_normal;
26286                             self->parent->attacking = 0;
26287                             self->parent->idling = 0;
26288                             ent_set_anim(self->parent, ANI_GETBOOMERANGINAIR, 0);
26289                             kill(self);
26290                             return 1;
26291                         }
26292                     }
26293                 }
26294             }
26295 
26296 
26297             //debug_printf("cur_distx:%f velx:%f",current_distx,self->velocity.x);
26298             //debug_printf("acc:%f speed:%f",acc,self->modeldata.speed);
26299             //debug_printf("boomerang_loop:%d",self->boomerang_loop);
26300             //debug_printf("sortid:%d",self->sortid);
26301         }
26302     }
26303 
26304     if(self->modeldata.subject_to_wall && validanim(self, ANI_FALL))   // Added so projectiles bounce off blocked exits
26305     {
26306         int wall;
26307 
26308         if((wall = checkwall(self->position.x, self->position.z)) > 0 && self->position.y < level->walls[wall].height)
26309         {
26310             self->takeaction = common_fall;
26311             self->attacking = 0;
26312             self->health = 0;
26313             self->projectile = 0;
26314             self->velocity.x = (self->direction == DIRECTION_RIGHT) ? (-1.2) : 1.2;
26315             self->damage_on_landing = 0;
26316             toss(self, 2.5 + randf(1));
26317             set_fall(self, ATK_NORMAL, 0, self, 100000, 0, 0, 0, 0, 0, 0);
26318         }
26319     }
26320 
26321     return 1;
26322 }
26323 
26324 // for common bomb types
bomb_move()26325 int bomb_move()
26326 {
26327     if(inair(self) && self->toexplode == 1)
26328     {
26329         if(self->direction == DIRECTION_LEFT)
26330         {
26331             self->velocity.x = -self->modeldata.speed;
26332         }
26333         else if(self->direction == DIRECTION_RIGHT)
26334         {
26335             self->velocity.x = self->modeldata.speed;
26336         }
26337     }
26338     else if(self->takeaction != bomb_explode)
26339     {
26340 
26341         self->takeaction = bomb_explode;
26342         self->velocity.y = 0;    // Stop moving up/down
26343         self->modeldata.no_adjust_base = 1;    // Stop moving up/down
26344         self->base = self->position.y;
26345         self->velocity.x = self->velocity.z = 0;
26346 
26347         if(self->modeldata.diesound >= 0)
26348         {
26349             sound_play_sample(self->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100);
26350         }
26351 
26352         if(self->toexplode == 2 && validanim(self, ANI_ATTACK2))
26353         {
26354             ent_set_anim(self, ANI_ATTACK2, 0);    // If bomb never reaces the ground, play this
26355         }
26356         else if (validanim(self, ANI_ATTACK1))
26357         {
26358             ent_set_anim(self, ANI_ATTACK1, 0);
26359         }
26360         // hit something, just make it an explosion animation.
26361         self->modeldata.subject_to_wall = 0;
26362         self->modeldata.subject_to_platform = 1;
26363         self->modeldata.subject_to_hole = 0;
26364     }
26365     return 1;
26366 }
26367 
star_move()26368 int star_move()
26369 {
26370     if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80) || (self->base <= 0 && !self->modeldata.falldie))
26371     {
26372         kill(self);
26373         return 0;
26374     }
26375 
26376     self->base -= 4;
26377     self->position.y = self->base;
26378 
26379     if(validanim(self, ANI_FALL))   // Added so projectiles bounce off blocked exits
26380     {
26381         int wall;
26382 
26383         if((wall = checkwall(self->position.x, self->position.z)) >= 0 && self->position.y < level->walls[wall].height)
26384         {
26385             self->takeaction = common_fall;
26386             self->attacking = 0;
26387             self->health = 0;
26388             self->projectile = 0;
26389             self->velocity.x = (self->direction == DIRECTION_RIGHT) ? (-1.2) : 1.2;
26390             self->damage_on_landing = 0;
26391             toss(self, 2.5 + randf(1));
26392             set_fall(self, ATK_NORMAL, 0, self, 100000, 0, 0, 0, 0, 0, 0);
26393         }
26394     }
26395 
26396     if(self->landed_on_platform || self->base <= 0)
26397     {
26398         self->takeaction = common_lie;
26399         self->health = 0;
26400         if(self->modeldata.nodieblink == 2)
26401         {
26402             self->animating = 0;
26403         }
26404     }
26405 
26406     return 1;
26407 }
26408 
26409 
26410 //dispatch move patterns
26411 //root function for aimove
common_move()26412 int common_move()
26413 {
26414     int aimove, makestop = 0, reachx, reachz;
26415     int air = inair(self);
26416     entity *other = NULL; //item
26417     entity *target = NULL;//hostile target
26418     entity *owner = NULL;
26419     entity *ent = NULL;
26420     int predir, stall;
26421     int patx[5], pxc, px, patz[5], pzc, pz, fz; //move pattern in z and x
26422 
26423     if(self->modeldata.aimove == -1)
26424     {
26425         return 0;    // invalid value
26426     }
26427 
26428     // get move pattern
26429     aimove = self->modeldata.aimove & MASK_AIMOVE1;
26430 
26431 //if(stricmp(self->name, "os")==0) printf("%d\n", aimove);
26432     // old and outdated patterns, but MUST be kept anyway
26433     if(aimove & AIMOVE1_BIKER)
26434     {
26435         // for biker subtype
26436         return biker_move();
26437     }
26438     else if(aimove & AIMOVE1_ARROW)
26439     {
26440         // for common straight-flying arrow
26441         return arrow_move();
26442     }
26443     else if(aimove & AIMOVE1_STAR)
26444     {
26445         // for a star, disappear when hit ground
26446         return star_move();
26447     }
26448     else if(aimove & AIMOVE1_BOMB)
26449     {
26450         // for a bomb, travel in a arc
26451         return bomb_move();
26452     }
26453     else if(aimove & AIMOVE1_BOOMERANG)
26454     {
26455         // for a boomerang, boomerang move
26456         return boomerang_move();
26457     }
26458     else if(aimove & AIMOVE1_NOMOVE)
26459     {
26460         // no move, just return
26461         return 0;
26462     }
26463     else
26464     {
26465         // all above are special move patterns, real AI goes here:
26466 
26467         // skip if the entity is in air,
26468         // removing this and entity might be spawned walking in air
26469         if(air)
26470         {
26471             return 0;
26472         }
26473 
26474         // store this for turning checking
26475         predir = self->direction;
26476 
26477         // find all possible entities for target
26478         // bad for optimization, but makes better sense
26479         if(self->custom_target == NULL || !self->custom_target->exists ) target = normal_find_target(-1, 0); // confirm the target again
26480         else target = self->custom_target;
26481 
26482         //other = ((time / GAME_SPEED + self->health / 3 + self->sortid) % 15 < 10) ? normal_find_item() : NULL; // find an item
26483         other = ( (time / GAME_SPEED + self->health / 3 + 1000) % 15 < 10) ? normal_find_item() : NULL; // find an item
26484         owner = self->parent;
26485 
26486         // temporary solution to turn off running if xdir is not set
26487         // unless one day vertical running logic is written
26488         if(!self->velocity.x)
26489         {
26490             self->running = 0;
26491         }
26492 
26493         // change direction unless the ai pattern ignores target or model has noflip
26494         if(!self->modeldata.noflip && !self->running && aimove != AIMOVE1_WANDER)
26495         {
26496             if(other)   //try to pick up an item, if possible
26497             {
26498                 self->direction = (self->position.x < other->position.x);
26499             }
26500             else if(target)
26501             {
26502                 self->direction = (self->position.x < target->position.x);
26503             }
26504             else if(owner)
26505             {
26506                 self->direction = (self->position.x < owner->position.x);
26507             }
26508         }
26509         else if(aimove == AIMOVE1_WANDER)
26510         {
26511             if(self->velocity.x)
26512             {
26513                 self->direction = (self->velocity.x > 0);
26514             }
26515         }
26516 
26517         //turn back if we have a turn animation
26518         // TODO, make a function for ai script
26519         if(self->direction != predir && validanim(self, ANI_TURN))
26520         {
26521             self->takeaction = common_turn;
26522             self->direction = !self->direction;
26523             self->velocity.x = self->velocity.z = 0;
26524             ent_set_anim(self, ANI_TURN, 0);
26525             return 1;
26526         }
26527 
26528         //pick up the item if possible
26529         if(other && diff(other->position.x, self->position.x) < (self->modeldata.grabdistance * 0.83333)
26530                 && diff(other->position.z, self->position.z) < (self->modeldata.grabdistance / 3) &&
26531                 other->animation->vulnerable[other->animpos])//won't pickup an item that is not previous one
26532         {
26533             if(diff(self->base, other->position.y) < 0.1)
26534             {
26535                 common_pickupitem(other);
26536                 return 1;
26537             }
26538         }
26539 
26540         if(self->modeldata.nomove)
26541         {
26542             self->idling = 1;
26543             return 1;
26544         }
26545 
26546         if(common_try_jump())
26547         {
26548             self->numwaypoints = 0;
26549             return 1;  //need to jump? so quit
26550         }
26551 
26552         if(checkpathblocked())
26553         {
26554             return 1;    // handle path blocked logic
26555         }
26556 
26557         // judge next move if stalltime is expired
26558         // skip if waypoints presents (passive move)
26559         if(self->stalltime < time && !self->waypoints)
26560         {
26561             if(other)
26562             {
26563                 // try walking to the item
26564                 common_try_pick(other);
26565                 ent = other;
26566             }
26567             else
26568             {
26569                 if(target && (self->modeldata.subtype == SUBTYPE_CHASE ||
26570                               ((self->modeldata.type & TYPE_NPC) && self->parent)))
26571                     // try chase a target
26572                 {
26573                     aimove |= AIMOVE1_CHASE;
26574                 }
26575 
26576                 if(aimove & AIMOVE1_CHASE)
26577                 {
26578                     aimove |= AIMOVE1_CHASEX | AIMOVE1_CHASEZ;
26579                 }
26580                 if(aimove & AIMOVE1_AVOID)
26581                 {
26582                     aimove |= AIMOVE1_AVOIDX | AIMOVE1_AVOIDZ;
26583                 }
26584 
26585                 if(other != ent)
26586                 {
26587                     self->velocity.x = self->velocity.z = 0;
26588                 }
26589 
26590                 if(!aimove && target)
26591                 {
26592                     common_try_wander(target, 1, 1);
26593                     ent = target;
26594                 }
26595                 else if ( target && (!(self->modeldata.aimove&AIMOVE2_NOTARGETIDLE) || ((self->modeldata.aimove&AIMOVE2_NOTARGETIDLE) && target->animnum != ANI_IDLE)) )
26596                 {
26597                     ent = target;
26598                     pxc = pzc = 0;
26599 
26600                     if(aimove & AIMOVE1_WANDER)
26601                     {
26602                         patx[pxc++] = AIMOVE1_WANDER;
26603                         patx[pxc++] = AIMOVE1_WANDER;
26604                         patz[pzc++] = AIMOVE1_WANDER;
26605                         patz[pzc++] = AIMOVE1_WANDER;
26606                     }
26607                     if(aimove & AIMOVE1_CHASEX)
26608                     {
26609                         patx[pxc++] = AIMOVE1_CHASEX;
26610                     }
26611                     if(aimove & AIMOVE1_AVOIDX)
26612                     {
26613                         patx[pxc++] = AIMOVE1_AVOIDX;
26614                     }
26615                     if(aimove & AIMOVE1_CHASEZ)
26616                     {
26617                         patz[pzc++] = AIMOVE1_CHASEZ;
26618                     }
26619                     if(aimove & AIMOVE1_AVOIDZ)
26620                     {
26621                         patz[pzc++] = AIMOVE1_AVOIDZ;
26622                     }
26623                     if(!pxc)
26624                     {
26625                         patx[pxc++] = AIMOVE1_WANDER;
26626                     }
26627                     if(!pzc)
26628                     {
26629                         patz[pzc++] = AIMOVE1_WANDER;
26630                     }
26631                     px = patx[(rand32() & 0xff) % pxc];
26632                     pz = patz[(rand32() & 0xff) % pzc];
26633 
26634                     fz = 0;
26635 
26636                     aimove = (self->modeldata.aimove & MASK_AIMOVE1);
26637 
26638                     //valid types: avoidx, aviodz, chasex, chasez, wander
26639                     if(px == AIMOVE1_WANDER)
26640                     {
26641                         if (pz == AIMOVE1_WANDER && aimove == AIMOVE1_WANDER)
26642                         {
26643                             common_try_wandercompletely(1, 1);
26644                             fz = 1;
26645                         }
26646                         else
26647                         {
26648                             common_try_wander(target, 1, 0);
26649                         }
26650 
26651                     }
26652                     else if(px == AIMOVE1_CHASEX)
26653                     {
26654                         common_try_chase(target, 1, (pz == AIMOVE1_CHASEZ));
26655                         fz = (pz == AIMOVE1_CHASEZ);
26656                     }
26657                     else if (px == AIMOVE1_AVOIDX)
26658                     {
26659                         common_try_avoid(target, 1, (pz == AIMOVE1_AVOIDZ));
26660                         fz = (pz == AIMOVE1_AVOIDZ);
26661                     }
26662                     if(!fz)
26663                     {
26664                         if(pz == AIMOVE1_WANDER)
26665                         {
26666                             common_try_wander(target, 0, 1);
26667                         }
26668                         else if(pz == AIMOVE1_CHASEZ)
26669                         {
26670                             common_try_chase(target, 0, 1);
26671                         }
26672                         else if (pz == AIMOVE1_AVOIDZ)
26673                         {
26674                             common_try_avoid(target, 0, 1);
26675                         }
26676                     }
26677 
26678                 }
26679                 else if(!common_try_follow(owner, 1, 1) && !(self->modeldata.aimove & AIMOVE2_NOTARGETIDLE) )
26680                 {
26681                     common_try_wandercompletely(1, 1);
26682                     ent = NULL;
26683                 }
26684                 else
26685                 {
26686                     ent = owner;
26687                 }
26688             }
26689             //end of if
26690 
26691         }//if(self->stalltime < time )
26692         else
26693         {
26694             ent = other;
26695             if(!ent)
26696             {
26697                 ent = target;
26698             }
26699             if(!ent)
26700             {
26701                 ent = owner;
26702             }
26703         }
26704         if(self->numwaypoints == 0 && self->waypoints)
26705         {
26706             free(self->waypoints);
26707             self->waypoints = NULL;
26708         }
26709 
26710         //fix 2d level panic, or should this be moved to the very beginning?
26711         if(self->modeldata.subject_to_minz > 0 && self->destz < PLAYER_MIN_Z)
26712         {
26713             self->destz = PLAYER_MIN_Z;
26714         }
26715         if(self->modeldata.subject_to_maxz > 0 && self->destz > PLAYER_MAX_Z)
26716         {
26717             self->destz = PLAYER_MAX_Z;
26718         }
26719 
26720         // don't run in passive move mode. The path could be complex and running may look bad.
26721         if(self->waypoints)
26722         {
26723             self->running = 0;
26724         }
26725 
26726         if(self->direction == (self->destx < self->position.x))
26727         {
26728             self->running = 0;
26729         }
26730 
26731         // make the entity walks in a straight path instead of flickering here and there
26732         // acceleration can be added easily based on this logic, if necessary
26733         adjustspeed(self->running ? self->modeldata.runspeed : self->modeldata.speed, self->position.x, self->position.z, self->destx, self->destz, &self->velocity.x, &self->velocity.z);
26734 
26735         // fix running animation, if the model doesn't allow running updown then set zdir to 0
26736         if(self->running && !self->modeldata.runupdown)
26737         {
26738             self->velocity.z = 0;
26739             self->destz = self->position.z;
26740         }
26741 
26742         // check destination point to make a stop or pop a waypoint from the stack
26743         reachx = (diff(self->position.x, self->destx) < MAX(1, ABS(self->velocity.x)));
26744         reachz = (diff(self->position.z, self->destz) < MAX(1, ABS(self->velocity.z)));
26745 
26746         // check destination point to make a stop or pop a waypoint from the stack
26747         if(reachx && reachz)
26748         {
26749             if(self->waypoints && self->numwaypoints)
26750             {
26751                 self->destx = self->waypoints[self->numwaypoints - 1].x;
26752                 self->destz = self->waypoints[self->numwaypoints - 1].z;
26753                 self->numwaypoints--;
26754             }
26755             else if(self->velocity.x || self->velocity.z)
26756             {
26757                 makestop = 1;
26758             }
26759         }
26760 
26761         if(!self->waypoints || !self->numwaypoints)
26762         {
26763             if(reachx)
26764             {
26765                 self->velocity.x = 0;
26766                 self->destx = self->position.x;
26767             }
26768             if(reachz)
26769             {
26770                 self->velocity.z = 0;
26771                 self->destz = self->position.z;
26772             }
26773         }
26774 
26775         // stoped so play idle, preventinng funny stepping bug, but may cause flickering
26776         if(!self->velocity.x && !self->velocity.z && !self->waypoints)
26777         {
26778             set_idle(self);
26779             if(makestop)
26780             {
26781                 stall = (GAME_SPEED - self->modeldata.aggression) / 2;
26782                 if(stall < GAME_SPEED / 5)
26783                 {
26784                     stall = GAME_SPEED / 5;
26785                 }
26786                 self->stalltime = time + MAX(0, stall);
26787             }
26788         }
26789         else
26790         {
26791             // readjust walk animation
26792             adjust_walk_animation(ent);
26793             // give proper stalltime if destination point is not reached
26794             // if the destination point is not reachable,
26795             // it should be already handled in checkpathblocked
26796             if(time > self->stalltime)
26797             {
26798                 if(ABS(self->velocity.x) > ABS(self->velocity.z))
26799                 {
26800                     stall = diff(self->destx, self->position.x) / ABS(self->velocity.x) * 2;
26801                 }
26802                 else if(self->velocity.z)
26803                 {
26804                     stall = diff(self->destz, self->position.z) / ABS(self->velocity.z) * 2;
26805                 }
26806                 else
26807                 {
26808                     stall = GAME_SPEED / 2;
26809                 }
26810                 self->stalltime = time + MAX(0, stall);
26811             }
26812         }
26813 
26814         //target is moving?  readjust destination sooner
26815         if(aimove != AIMOVE1_WANDER && !self->waypoints && ent && (self->velocity.x || self->velocity.z) && (ent->velocity.x || ent->velocity.z))
26816         {
26817             if(self->running && self->stalltime > time + GAME_SPEED / 2)
26818             {
26819                 self->stalltime = time + GAME_SPEED / 2;
26820             }
26821             else if(!self->running && self->stalltime > time + GAME_SPEED / 5)
26822             {
26823                 self->stalltime = time + GAME_SPEED / 5;
26824             }
26825         }
26826 
26827         return 1;
26828 
26829     }
26830 
26831     return 1;
26832 }
26833 
26834 
decide_stalker()26835 void decide_stalker()
26836 {
26837     entity *ent, *furthest = NULL;
26838     int i;
26839     int l = 0, r = 0;
26840     float maxz = 0.0f, z;
26841 
26842     if(stalker && stalking)
26843     {
26844         return;
26845     }
26846 
26847     firstplayer = NULL;
26848 
26849     for(i = 0; i < 4; i++)
26850     {
26851         if(player[i].ent)
26852         {
26853             firstplayer = player[i].ent;
26854             break;
26855         }
26856     }
26857 
26858     if(!firstplayer)
26859     {
26860         return;
26861     }
26862 
26863     for(i = 0; i < ent_max; i++)
26864     {
26865         ent = ent_list[i];
26866 
26867         if(ent->exists && !ent->dead && (ent->modeldata.type & TYPE_ENEMY))
26868         {
26869             if(ent->position.x > firstplayer->position.x)
26870             {
26871                 r++;
26872             }
26873             else
26874             {
26875                 l++;
26876             }
26877 
26878             if((z = diff(ent->position.z, firstplayer->position.z)) >= maxz &&
26879                     (ent->modeldata.aimove == 0 || (ent->modeldata.aimove & AIMOVE1_CHASE))) // 2 mostly used type
26880             {
26881                 maxz = z;
26882                 furthest = ent;
26883             }
26884         }
26885     }
26886 
26887     if((l > 1 && !r) || (r > 1 && !l))
26888     {
26889         stalker = furthest;
26890         //printf("** stalker decided: %s @ time %d\n", stalker->name, time);
26891     }
26892 }
26893 
26894 
plan()26895 void plan()
26896 {
26897     decide_stalker();
26898 }
26899 
checkstalker()26900 void checkstalker()
26901 {
26902     float maxspeed;
26903     int running;
26904 
26905     if(self != stalker)
26906     {
26907         return;
26908     }
26909 
26910     if(!firstplayer)
26911     {
26912         stalker = NULL;
26913         return;
26914     }
26915 
26916     if(stalking)
26917     {
26918         if(self->stalltime <= time)
26919         {
26920             //printf("** stalk time expired: %s @ time %d\n", stalker->name, time);
26921             stalker = NULL;
26922         }
26923         return;
26924     }
26925 
26926     running = validanim(self, ANI_RUN);
26927 
26928     maxspeed = running ? self->modeldata.runspeed : self->modeldata.speed;
26929 
26930     self->velocity.x = maxspeed;
26931     self->velocity.z = 0;
26932 
26933     if(self->position.x > firstplayer->position.x)
26934     {
26935         self->velocity.x = -self->velocity.x;
26936     }
26937 
26938     self->running = running;
26939 
26940 
26941     self->stalltime = time + (diff(self->position.x, firstplayer->position.x) + 150) / maxspeed * THINK_SPEED;
26942 
26943     adjust_walk_animation(firstplayer);
26944 
26945     stalking = 1;
26946     //printf("**start stalking: %s @ time %d till @%d\n", stalker->name, time, self->stalltime);
26947 }
26948 
checkplanned()26949 int checkplanned()
26950 {
26951     return 0;
26952 }
26953 
26954 
ai_check_warp()26955 int ai_check_warp()
26956 {
26957     if(self->link)
26958     {
26959         return 0;
26960     }
26961 
26962     if(self->modeldata.subtype == SUBTYPE_FOLLOW && self->parent && validanim(self, ANI_IDLE) &&
26963             (diff(self->position.z, self->parent->position.z) > self->modeldata.animation[ANI_IDLE]->range.max.x ||
26964              diff(self->position.x, self->parent->position.x) > self->modeldata.animation[ANI_IDLE]->range.max.x) )
26965     {
26966         self->takeaction = npc_warp;
26967         return 1;
26968     }
26969     return 0;
26970 }
26971 
ai_check_lie()26972 int ai_check_lie()
26973 {
26974     if(self->drop && self->position.y == self->base && !self->velocity.y && validanim(self, ANI_RISEATTACK) && ((rand32() % (self->stalltime - time + 1)) < 3) && (self->health > 0 && time > self->staydown.riseattack_stall))
26975     {
26976         common_try_riseattack();
26977         return 1;
26978     }
26979     return 0;
26980 }
26981 
ai_check_grabbed()26982 int ai_check_grabbed()
26983 {
26984     if(self->link && !self->grabbing && !self->inpain && self->takeaction != common_prethrow && !inair(self) &&
26985             time >= self->stalltime && validanim(self, ANI_SPECIAL))
26986     {
26987         check_special();
26988         return 1;
26989     }
26990     return 0;
26991 }
26992 
ai_check_grab()26993 int ai_check_grab()
26994 {
26995     if(self->grabbing && !self->attacking)
26996     {
26997         common_grab_check();
26998         return 1;
26999     }
27000     return 0;
27001 }
ai_check_escape()27002 int ai_check_escape()
27003 {
27004     if((self->escapecount > self->modeldata.escapehits) && !inair(self) && validanim(self, ANI_SPECIAL2))
27005     {
27006         // Counter the player!
27007         check_costmove(ANI_SPECIAL2, 0, 0);
27008         return 1;
27009     }
27010     return 0;
27011 }
27012 
ai_check_busy()27013 int ai_check_busy()
27014 {
27015     return self->link || !self->idling;
27016 }
27017 
27018 
27019 // A.I root
common_think()27020 void common_think()
27021 {
27022 
27023     if(self->dead)
27024     {
27025         return;
27026     }
27027 
27028     //if(checkplanned()) return;
27029 
27030     // too far away , do a warp
27031     if(ai_check_warp())
27032     {
27033         return;
27034     }
27035 
27036     // rise? try rise attack
27037     if(ai_check_lie())
27038     {
27039         return;
27040     }
27041 
27042     // Escape?
27043     if(ai_check_grabbed())
27044     {
27045         return;
27046     }
27047 
27048     //grabbing something
27049     if(ai_check_grab())
27050     {
27051         return;
27052     }
27053 
27054     // Enemies can now escape non-knockdown spammage (What a weird phrase)!
27055     if(ai_check_escape())
27056     {
27057         return;
27058     }
27059 
27060     // busy right now?
27061     if(ai_check_busy())
27062     {
27063         return;
27064     }
27065 
27066     // idle, so try to attack or judge next move
27067     // dont move if fall into a hole or off a wall
27068     if(common_attack())
27069     {
27070         return;
27071     }
27072     common_move();
27073 }
27074 
27075 //////////////////////////////////////////////////////////////////////////
27076 
suicide()27077 void suicide()
27078 {
27079     if(time < self->stalltime)
27080     {
27081         return;
27082     }
27083     level_completed |= self->boss;
27084     kill(self);
27085 }
27086 
27087 
27088 
27089 // Re-enter playfield
27090 // Used by player_fall and player_takedamage
player_die()27091 void player_die()
27092 {
27093     int playerindex = self->playerindex;
27094     if(!livescheat)
27095     {
27096         --player[playerindex].lives;
27097     }
27098 
27099     if(firstplayer == self)
27100     {
27101         firstplayer = NULL;
27102     }
27103 
27104     execute_pdie_script(playerindex);
27105 
27106     if(nomaxrushreset[4] >= 1)
27107     {
27108         nomaxrushreset[playerindex] = player[playerindex].ent->rush.count.max;
27109     }
27110     player[playerindex].ent = NULL;
27111     player[playerindex].spawnhealth = self->modeldata.health;
27112     player[playerindex].spawnmp = self->modeldata.mp;
27113 
27114 
27115     if(self->modeldata.nodieblink != 3)
27116     {
27117         kill(self);
27118     }
27119     else
27120     {
27121         self->think = NULL;
27122         self->takeaction = NULL;
27123         self->modeldata.type = TYPE_NONE;
27124     }
27125 
27126     if(player[playerindex].lives <= 0)
27127     {
27128         if(!player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent)
27129         {
27130             timeleft = 10 * COUNTER_SPEED;
27131             if(!player[0].joining && !player[1].joining && !player[0].joining && !player[0].joining &&
27132                     ((!noshare && credits < 1) || (noshare && player[0].credits < 1 && player[1].credits < 1 && player[2].credits < 1 && player[3].credits < 1)) )
27133             {
27134                 timeleft = COUNTER_SPEED / 2;
27135             }
27136         }
27137         if(self->modeldata.weaploss[0] == WEAPLOSS_TYPE_CHANGE)
27138         {
27139             player[playerindex].weapnum = level->setweap;
27140         }
27141         if(nomaxrushreset[4] != 2)
27142         {
27143             nomaxrushreset[playerindex] = 0;
27144         }
27145         return;
27146     }
27147     else
27148     {
27149         spawnplayer(playerindex);
27150         execute_respawn_script(playerindex);
27151         if(!nodropen)
27152         {
27153             control_rumble(playerindex, 125);
27154             drop_all_enemies();
27155         }
27156     }
27157 
27158     if(!level->noreset)
27159     {
27160         timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
27161     }
27162 
27163 }
27164 
27165 
27166 
player_trymove(float xdir,float zdir)27167 int player_trymove(float xdir, float zdir)
27168 {
27169     return common_trymove(xdir, zdir);
27170 }
27171 
check_energy(e_cost_check which,int ani)27172 int check_energy(e_cost_check which, int ani)
27173 {
27174     int result = FALSE;
27175     e_entity_type type;        //Entity type.
27176     s_energycost energycost;
27177 
27178     energycost.cost     = 0;
27179     energycost.disable  = 0;
27180     energycost.mponly   = COST_TYPE_MP_THEN_HP;
27181 
27182     // Get animation's energycost if available.
27183     if(self->modeldata.animation[ani]->energycost)
27184     {
27185         energycost = *self->modeldata.animation[ani]->energycost;
27186     }
27187 
27188     //if (!self->modeldata.animation[ani]->energycost) return TRUE;
27189 
27190     if(self->modeldata.animation[ani])
27191     {
27192         // Get entity type.
27193         type	   = self->modeldata.type;
27194 
27195         // Caskey, Damon V.
27196         // 2010-05-08
27197         //
27198         // It is now possible to individually disable specials. In
27199         // many cases (weapons in particular) this can	help cut down the need for
27200         // superfluous models when differing abilities are desired for players,
27201         // enemies, or npcs.
27202         if(!(energycost.disable == type													// Disabled by type?
27203                 || (energycost.disable == -1)											    // Disabled for all?
27204                 || (energycost.disable == -2 && (type & (TYPE_ENEMY  | TYPE_NPC)))		    // Disabled for all AI?
27205                 || (energycost.disable == -3 && (type & (TYPE_PLAYER | TYPE_NPC)))	        // Disabled for players & NPCs?
27206                 || (energycost.disable == -4 && (type & (TYPE_PLAYER | TYPE_ENEMY)))))     // Disabled for all AI?
27207         {
27208             // No seal or seal is less/same as energy cost?
27209             if(!self->seal || self->seal >= energycost.cost)
27210             {
27211                 if(validanim(self, ani) &&										    //Validate the animation one more time.
27212                         ((which == COST_CHECK_MP &&			                    //Check magic validity
27213                           (energycost.mponly != COST_TYPE_HP_ONLY) &&
27214                           (self->mp >= energycost.cost)) ||
27215                          (which == COST_CHECK_HP &&			                    //Check health validity
27216                           (energycost.mponly != COST_TYPE_MP_ONLY) &&
27217                           (self->health > energycost.cost))))
27218                 {
27219                     result = TRUE;
27220                 }
27221                 else
27222                 {
27223                     //DC 2009-01-23
27224                     //Tried putting the CANT animation here to keep code compacted, but won't work. I'll come back to this.
27225                     //if (validanim(self,ani)){
27226                     //    ent_set_anim(self, ANI_CANT, 0);
27227                     //    self->takeaction = common_attack_proc;
27228                     //    player[self->playerindex].playkeys = 0;
27229                     //}
27230                 }
27231             }
27232         }
27233     }
27234 
27235     return result;
27236 }
27237 
27238 
check_special()27239 int check_special()
27240 {
27241     entity *e;
27242     if((!level->nospecial || level->nospecial == 3) &&
27243             !self->cantfire && validanim(self, ANI_SPECIAL) &&
27244             (check_energy(COST_CHECK_HP, ANI_SPECIAL) || check_energy(COST_CHECK_MP, ANI_SPECIAL))
27245        )
27246     {
27247         self->takeaction = common_attack_proc;
27248         set_attacking(self);
27249         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
27250 
27251         e = self->link;
27252         if(e)
27253         {
27254             e->takeaction = NULL;
27255             ent_unlink(self);
27256             set_idle(e);
27257         }
27258 
27259         if(self->modeldata.smartbomb && !self->modeldata.dofreeze)
27260         {
27261             smart_bomb(self, self->modeldata.smartbomb); // do smartbomb immediately if it doesn't freeze screen
27262         }
27263 
27264         self->running = 0;    // If special is executed while running, ceases to run
27265         self->velocity.x = self->velocity.z = 0;
27266         ent_set_anim(self, ANI_SPECIAL, 0);
27267 
27268         if(self->modeldata.dofreeze)
27269         {
27270             smartbomber = self;    // Freezes the animations of all enemies/players while special is executed
27271         }
27272 
27273         if(!nocost && !healthcheat)
27274         {
27275             // Energycost defined?
27276             if(self->modeldata.animation[ANI_SPECIAL]->energycost)
27277             {
27278                 if(check_energy(COST_CHECK_MP, ANI_SPECIAL))
27279                 {
27280                     self->mp -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
27281                 }
27282                 else
27283                 {
27284                     self->health -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
27285                 }
27286             }
27287         }
27288 
27289         return 1;
27290     }
27291     return 0;
27292 }
27293 
27294 
27295 // Check keys for special move. Used several times, so I func'd it.
27296 // 1-10-05 changed self->health>6 to self->health > self->modeldata.animation[ANI_SPECIAL]->energycost.cost
player_check_special()27297 int player_check_special()
27298 {
27299     u32 thekey = 0;
27300     if((!ajspecial || (ajspecial && !validanim(self, ANI_BLOCK))) &&
27301             (player[self->playerindex].playkeys & FLAG_SPECIAL))
27302     {
27303         thekey = FLAG_SPECIAL;
27304     }
27305     else if(ajspecial && ((player[self->playerindex].playkeys & FLAG_JUMP) &&
27306                           (player[self->playerindex].keys & FLAG_ATTACK)))
27307     {
27308         thekey = FLAG_JUMP;
27309     }
27310     else
27311     {
27312         return 0;
27313     }
27314 
27315     if(check_special())
27316     {
27317         self->stalltime = 0;
27318         player[self->playerindex].playkeys &= ~thekey;
27319         return 1;
27320     }
27321     else
27322     {
27323         return 0;
27324     }
27325 }
27326 
27327 
common_land()27328 void common_land()
27329 {
27330     self->velocity.x = self->velocity.z = 0;
27331     if(self->animating)
27332     {
27333         return;
27334     }
27335 
27336     self->takeaction = NULL;
27337     set_idle(self);
27338 }
27339 
27340 
27341 //animal run when you lost it 3 times by tails
runanimal()27342 void runanimal()
27343 {
27344     common_walk_anim(self);
27345     //ent_set_anim(self, ANI_WALK, 0);
27346 
27347     if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80))
27348     {
27349         kill(self);
27350         return;
27351     }
27352 
27353     if(self->direction == DIRECTION_RIGHT)
27354     {
27355         self->position.x += self->modeldata.speed;
27356     }
27357     else
27358     {
27359         self->position.x -= self->modeldata.speed;
27360     }
27361 }
27362 
27363 
player_blink()27364 void player_blink()
27365 {
27366     self->blink = 1;
27367     if(time >= self->stalltime)
27368     {
27369         player_die();
27370     }
27371 }
27372 
27373 
common_grabattack()27374 void common_grabattack()
27375 {
27376     if(self->animating)
27377     {
27378         return;
27379     }
27380 
27381     self->attacking = 0;
27382 
27383     if(!(self->combostep[0] || self->combostep[1] ||
27384             self->combostep[2] || self->combostep[3] ||
27385             self->combostep[4]))
27386     {
27387         ent_unlink(self);
27388     }
27389 
27390     if(self->link)
27391     {
27392         self->takeaction = common_grab;
27393         self->link->takeaction = common_grabbed;
27394         self->attacking = 0;
27395         ent_set_anim(self, ANI_GRAB, 0);
27396         set_pain(self->link, -1, 0);
27397         update_frame(self, self->animation->numframes - 1);
27398         update_frame(self->link, self->link->animation->numframes - 1);
27399     }
27400     else
27401     {
27402         self->takeaction = NULL;
27403         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
27404         set_idle(self);
27405     }
27406 }
27407 
27408 // Function that causes the player to continue to move up or down until the animation has finished playing
common_dodge()27409 void common_dodge()    // New function so players can dodge with up up or down down
27410 {
27411     if(self->animating)    // Continues to move as long as the player is animating
27412     {
27413         return;
27414     }
27415     else    // Once done animating, returns to thinking
27416     {
27417         self->takeaction = NULL;
27418         self->velocity.x = self->velocity.z = 0;
27419         set_idle(self);
27420     }
27421 }
27422 
27423 
common_prejump()27424 void common_prejump()
27425 {
27426     if(self->animating)
27427     {
27428         return;
27429     }
27430     dojump(self->jump.velocity.y, self->jump.velocity.x, self->jump.velocity.z, self->jump.id);
27431 }
27432 
27433 
tryjump(float jumpv,float jumpx,float jumpz,int jumpid)27434 void tryjump(float jumpv, float jumpx, float jumpz, int jumpid)
27435 {
27436     self->jump.velocity.y = jumpv;
27437     self->jump.velocity.x = jumpx;
27438     self->jump.velocity.z = jumpz;
27439     self->jump.id = jumpid;
27440     if(validanim(self, ANI_JUMPDELAY))
27441     {
27442         self->takeaction = common_prejump;
27443         self->velocity.x = self->velocity.z = 0;
27444 
27445         self->idling = 0;
27446         ent_set_anim(self, ANI_JUMPDELAY, 0);
27447     }
27448     else
27449     {
27450         dojump(jumpv, jumpx, jumpz, jumpid);
27451     }
27452 }
27453 
27454 
dojump(float jumpv,float jumpx,float jumpz,int jumpid)27455 void dojump(float jumpv, float jumpx, float jumpz, int jumpid)
27456 {
27457     entity *dust;
27458 
27459     self->takeaction = common_jump;
27460 
27461     if(SAMPLE_JUMP >= 0)
27462     {
27463         sound_play_sample(SAMPLE_JUMP, 0, savedata.effectvol, savedata.effectvol, 100);
27464     }
27465 
27466     //Spawn jumpstart dust.
27467     if(self->modeldata.dust.jump_start >= 0)
27468     {
27469         dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_start, NULL);
27470         if(dust)
27471         {
27472             dust->base = self->position.y;
27473             dust->autokill = 2;
27474             execute_onspawn_script(dust);
27475         }
27476     }
27477 
27478     set_jumping(self);
27479 
27480     toss(self, jumpv);
27481 
27482     if(self->direction == DIRECTION_LEFT)
27483     {
27484         self->velocity.x = -jumpx;
27485     }
27486     else
27487     {
27488         self->velocity.x = jumpx;
27489     }
27490 
27491     self->velocity.z = jumpz;
27492     ent_set_anim(self, jumpid, 0);
27493 }
27494 
27495 // Function created to combine the action taken if either picking up an item, or running into an item that is a
27496 // SUBTYPE_TOUCH, executing the appropriate action based on which type of item is picked up
didfind_item(entity * other)27497 void didfind_item(entity *other)
27498 {
27499     // Function that takes care of items when picked up
27500     set_opponent(self, other);
27501 
27502     //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
27503     if(other->modeldata.reload)
27504     {
27505         if(self->weapent && self->weapent->modeldata.typeshot)
27506         {
27507             self->weapent->modeldata.shootnum += other->modeldata.reload;
27508             if(self->weapent->modeldata.shootnum > self->weapent->modeldata.shootnum)
27509             {
27510                 self->weapent->modeldata.shootnum = self->weapent->modeldata.shootnum;
27511             }
27512             if(SAMPLE_GET >= 0)
27513             {
27514                 sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
27515             }
27516         }
27517         else
27518         {
27519             addscore(self->playerindex, other->modeldata.score);
27520             if(SAMPLE_GET2 >= 0)
27521             {
27522                 sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
27523             }
27524         }
27525     }
27526     //end of weapons items section
27527     else if(other->modeldata.score)
27528     {
27529         addscore(self->playerindex, other->modeldata.score);
27530         if(SAMPLE_GET2 >= 0)
27531         {
27532             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
27533         }
27534     }
27535     else if(other->health)
27536     {
27537         self->health += other->health;
27538 
27539         if(self->health > self->modeldata.health)
27540         {
27541             self->health = self->modeldata.health;
27542         }
27543 
27544         other->health = 0;
27545 
27546         if(SAMPLE_GET >= 0)
27547         {
27548             sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
27549         }
27550     }
27551     else if(other->modeldata.mp)
27552     {
27553         self->mp += other->modeldata.mp;
27554 
27555         if(self->mp > self->modeldata.mp)
27556         {
27557             self->mp = self->modeldata.mp;
27558         }
27559 
27560         other->mp = 0;
27561         sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
27562     }
27563     else if(stricmp(other->modeldata.name, "Time") == 0)
27564     {
27565         timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
27566 
27567         if(SAMPLE_GET2 >= 0)
27568         {
27569             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
27570         }
27571     }
27572     else if(other->modeldata.makeinv)
27573     {
27574         // Mar 2, 2005 - New item makes player invincible
27575         self->invincible = 1;
27576         self->invinctime = time + ABS(other->modeldata.makeinv);
27577         self->blink = (other->modeldata.makeinv > 0);
27578 
27579         if(SAMPLE_GET2 >= 0)
27580         {
27581             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
27582         }
27583     }
27584     else if(other->modeldata.smartbomb)
27585     {
27586         // Damages everything on the screen
27587         smart_bomb(self, other->modeldata.smartbomb);
27588 
27589         if(SAMPLE_GET2 >= 0)
27590         {
27591             sound_play_sample(SAMPLE_GET2, 0, savedata.effectvol, savedata.effectvol, 100);
27592         }
27593     }
27594     else if(other->modeldata.subtype == SUBTYPE_WEAPON)
27595     {
27596         dropweapon(0);
27597         self->weapent = other;
27598         set_weapon(self, other->modeldata.weapnum, 0);
27599 
27600         if(self->modeldata.animal)  // UTunnels: well, ride, not get. :)
27601         {
27602             self->direction = other->direction;
27603             self->position.x = other->position.x;
27604             self->position.z = other->position.z;
27605         }
27606 
27607         if(!other->modeldata.typeshot && self->modeldata.typeshot)
27608         {
27609             other->modeldata.typeshot = 1;
27610         }
27611 
27612         if(SAMPLE_GET >= 0)
27613         {
27614             sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
27615         }
27616     }
27617     else if(other->modeldata.subtype == SUBTYPE_PROJECTILE)
27618     {
27619         dropweapon(0);
27620         self->weapent = other;
27621 
27622         if(SAMPLE_GET >= 0)
27623         {
27624             sound_play_sample(SAMPLE_GET, 0, savedata.effectvol, savedata.effectvol, 100);
27625         }
27626     }
27627     else if(other->modeldata.credit)
27628     {
27629         if(!noshare)
27630         {
27631             credits++;
27632         }
27633         else
27634         {
27635             player[self->playerindex].credits++;
27636         }
27637 
27638         if(SAMPLE_1UP >= 0)
27639         {
27640             sound_play_sample(SAMPLE_1UP, 0, savedata.effectvol, savedata.effectvol, 100);
27641         }
27642     }
27643     else
27644     {
27645         // Must be a 1up then.
27646         player[self->playerindex].lives++;
27647 
27648         if(SAMPLE_1UP >= 0)
27649         {
27650             sound_play_sample(SAMPLE_1UP, 0, savedata.effectvol, savedata.effectvol, 100);
27651         }
27652     }
27653 
27654     if(other->modeldata.subtype != SUBTYPE_WEAPON && other->modeldata.subtype != SUBTYPE_PROJECTILE)
27655     {
27656         other->takeaction = suicide;
27657         if(!other->modeldata.instantitemdeath)
27658         {
27659             other->nextthink = time + GAME_SPEED * 3;
27660         }
27661     }
27662     else
27663     {
27664         other->nextthink = other->nextanim = time + GAME_SPEED * 999999;
27665     }
27666     other->position.z = 100000;
27667 }
27668 
player_fall_check()27669 void player_fall_check()
27670 {
27671     if(autoland != 2 && (player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_JUMP)) == (FLAG_MOVEUP | FLAG_JUMP))
27672     {
27673         self->damage_on_landing = -2; // mark it, so we will play land animation when hit the ground
27674     }
27675 }
27676 
player_grab_check()27677 void player_grab_check()
27678 {
27679     entity *other = self->link;
27680 
27681     if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
27682     {
27683         return;
27684     }
27685 
27686     if(self->base != other->base)
27687     {
27688         // Change this from ->position.y to ->base
27689         self->takeaction = NULL;
27690         ent_unlink(self);
27691         set_idle(self);
27692         return;
27693     }
27694 
27695     if(player_check_special())
27696     {
27697         return;
27698     }
27699 
27700     if(!nolost && self->modeldata.weaploss[0] == WEAPLOSS_TYPE_ANY)
27701     {
27702         dropweapon(1);
27703     }
27704 
27705     // grabturn code
27706     if(self->animation == self->modeldata.animation[ANI_GRABTURN])
27707     {
27708         // still turning? don't bother with anything else
27709         if(self->animating)
27710         {
27711             return;
27712         }
27713 
27714         // done turning? switch directions and return to grab animation
27715         else
27716         {
27717             if(self->direction == DIRECTION_RIGHT)
27718             {
27719                 self->direction = DIRECTION_LEFT;
27720                 other->direction = DIRECTION_RIGHT;
27721             }
27722             else
27723             {
27724                 self->direction = DIRECTION_RIGHT;
27725                 other->direction = DIRECTION_LEFT;
27726             }
27727             other->position.x = self->position.x + (((self->direction * 2) - 1) * self->modeldata.grabdistance);
27728             ent_set_anim(self, ANI_GRAB, 0);
27729             set_pain(other, -1, 0);
27730             update_frame(self, self->animation->numframes - 1);
27731             update_frame(other, other->animation->numframes - 1);
27732         }
27733     }
27734 
27735     self->attacking = 0; //for checking
27736     self->grabwalking = 0;
27737     if(self->direction == DIRECTION_RIGHT ?
27738             (player[self->playerindex].keys & FLAG_MOVELEFT) :
27739             (player[self->playerindex].keys & FLAG_MOVERIGHT))
27740     {
27741         // initiating grabturn
27742         if(self->modeldata.grabturn)
27743         {
27744             // start animation if it exists...
27745             if(validanim(self, ANI_GRABTURN))
27746             {
27747                 ent_set_anim(self, ANI_GRABTURN, 0);
27748                 if(validanim(other, ANI_GRABBEDTURN))
27749                 {
27750                     ent_set_anim(other, ANI_GRABBEDTURN, 0);
27751                 }
27752                 else if(validanim(other, ANI_GRABBED))
27753                 {
27754                     ent_set_anim(other, ANI_GRABBED, 0);
27755                 }
27756                 else
27757                 {
27758                     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);
27759                     else ent_set_anim(other, ANI_PAIN, 0);
27760                 }
27761                 other->velocity.x = other->velocity.z = self->velocity.x = self->velocity.z = 0;
27762                 other->position.x = self->position.x;
27763                 return;
27764             }
27765 
27766             // otherwise, just turn around
27767             else
27768             {
27769                 if(self->direction == DIRECTION_RIGHT)
27770                 {
27771                     self->direction = DIRECTION_LEFT;
27772                     other->direction = DIRECTION_RIGHT;
27773                 }
27774                 else
27775                 {
27776                     self->direction = DIRECTION_RIGHT;
27777                     other->direction = DIRECTION_LEFT;
27778                 }
27779                 ent_set_anim(self, ANI_GRAB, 0);
27780                 set_pain(other, -1, 0);
27781                 update_frame(self, self->animation->numframes - 1);
27782                 update_frame(other, other->animation->numframes - 1);
27783                 other->position.x = self->position.x + (((self->direction * 2) - 1) * self->modeldata.grabdistance);
27784             }
27785         }
27786         else if(!validanim(self, ANI_GRABWALK) && time > self->releasetime)
27787         {
27788             // Release
27789             self->takeaction = NULL;
27790             ent_unlink(self);
27791             set_idle(self);
27792             return;
27793         }
27794     }
27795     else
27796     {
27797         self->releasetime = time + (GAME_SPEED / 2);
27798     }
27799 
27800     if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
27801             (self->direction ?
27802              (player[self->playerindex].keys & FLAG_MOVELEFT) :
27803              (player[self->playerindex].keys & FLAG_MOVERIGHT)))
27804     {
27805         player[self->playerindex].playkeys -= FLAG_ATTACK;
27806         if(validanim(self, ANI_GRABBACKWARD))
27807         {
27808             dograbattack(4);
27809         }
27810         else if(validanim(self, ANI_THROW))
27811         {
27812             if(self->modeldata.throwframewait >= 0)
27813             {
27814                 doprethrow();
27815             }
27816             else
27817             {
27818                 dothrow();
27819             }
27820         }
27821         else
27822         {
27823             dograbattack(0);
27824         }
27825     }
27826     // grab forward
27827     else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
27828             validanim(self, ANI_GRABFORWARD) &&
27829             (!self->direction ?
27830              (player[self->playerindex].keys & FLAG_MOVELEFT) :
27831              (player[self->playerindex].keys & FLAG_MOVERIGHT)))
27832     {
27833         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
27834         dograbattack(1);
27835     }
27836     // grab up
27837     else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
27838             validanim(self, ANI_GRABUP) && (player[self->playerindex].keys & FLAG_MOVEUP))
27839     {
27840         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
27841         dograbattack(2);
27842     }
27843     // grab down
27844     else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
27845             validanim(self, ANI_GRABDOWN) && (player[self->playerindex].keys & FLAG_MOVEDOWN))
27846     {
27847         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
27848         dograbattack(3);
27849     }
27850     // normal grab attack
27851     else if((player[self->playerindex].playkeys & FLAG_ATTACK) && validanim(self, ANI_GRABATTACK))
27852     {
27853         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
27854         dograbattack(0);
27855     }
27856     // grab attack finisher
27857     else if(player[self->playerindex].playkeys & (FLAG_JUMP | FLAG_ATTACK))
27858     {
27859         player[self->playerindex].playkeys &= ~(FLAG_JUMP | FLAG_ATTACK);
27860 
27861         // Perform final blow
27862         if(validanim(self, ANI_GRABATTACK2) || validanim(self, ANI_ATTACK3))
27863         {
27864             dograbattack(-1);
27865         }
27866         else
27867         {
27868             self->attacking = 1;
27869             memset(self->combostep, 0, sizeof(*self->combostep) * 5);
27870             self->takeaction = common_grabattack;
27871             tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, 0, ANI_JUMP);
27872         }
27873     }
27874 
27875     // grab walk code
27876     else if(validanim(self, ANI_GRABWALK) // check if grabwalk animation exists
27877 
27878             // if entity is still animating anything besides a grabwalk variant, don't let them move
27879             && (!self->animating || self->animation == self->modeldata.animation[ANI_GRABWALK]
27880                 || self->animation == self->modeldata.animation[ANI_GRABWALKUP]
27881                 || self->animation == self->modeldata.animation[ANI_GRABWALKDOWN]
27882                 || self->animation == self->modeldata.animation[ANI_GRABBACKWALK]))
27883     {
27884 
27885         // z axis movement
27886         if(PLAYER_MIN_Z != PLAYER_MAX_Z)
27887         {
27888             if(player[self->playerindex].keys & FLAG_MOVEUP)
27889             {
27890                 if(self->modeldata.grabwalkspeed)
27891                 {
27892                     self->velocity.z = -self->modeldata.grabwalkspeed / 2;
27893                 }
27894                 else
27895                 {
27896                     self->velocity.z = -self->modeldata.speed / 2;
27897                 }
27898             }
27899             else if(player[self->playerindex].keys & FLAG_MOVEDOWN)
27900             {
27901                 if(self->modeldata.grabwalkspeed)
27902                 {
27903                     self->velocity.z = self->modeldata.grabwalkspeed / 2;
27904                 }
27905                 else
27906                 {
27907                     self->velocity.z = self->modeldata.speed / 2;
27908                 }
27909             }
27910             else if(!(player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
27911             {
27912                 self->velocity.z = 0;
27913             }
27914         }
27915 
27916         // x axis movement
27917         if(player[self->playerindex].keys & FLAG_MOVELEFT)
27918         {
27919             if(self->modeldata.grabwalkspeed)
27920             {
27921                 self->velocity.x = -self->modeldata.grabwalkspeed;
27922             }
27923             else
27924             {
27925                 self->velocity.x = -self->modeldata.speed;
27926             }
27927         }
27928 
27929         else if(player[self->playerindex].keys & FLAG_MOVERIGHT)
27930         {
27931             if(self->modeldata.grabwalkspeed)
27932             {
27933                 self->velocity.x = self->modeldata.grabwalkspeed;
27934             }
27935             else
27936             {
27937                 self->velocity.x = self->modeldata.speed;
27938             }
27939         }
27940         else if(!((player[self->playerindex].keys & FLAG_MOVELEFT) || (player[self->playerindex].keys & FLAG_MOVERIGHT)) )
27941         {
27942             self->velocity.x = 0;
27943         }
27944 
27945         // setting the animations based on the velocity set above
27946         if(self->velocity.x || self->velocity.z)
27947         {
27948             if(((self->velocity.x > 0 && self->direction == DIRECTION_LEFT) || (self->velocity.x < 0 && self->direction == DIRECTION_RIGHT)) && validanim(self, ANI_GRABBACKWALK))
27949             {
27950                 ent_set_anim(self, ANI_GRABBACKWALK, 0);
27951             }
27952             else if(self->velocity.z < 0 && validanim(self, ANI_GRABWALKUP))
27953             {
27954                 ent_set_anim(self, ANI_GRABWALKUP, 0);
27955             }
27956             else if(self->velocity.z > 0 && validanim(self, ANI_GRABWALKDOWN))
27957             {
27958                 ent_set_anim(self, ANI_GRABWALKDOWN, 0);
27959             }
27960             else
27961             {
27962                 ent_set_anim(self, ANI_GRABWALK, 0);
27963             }
27964             if(self->animation == self->modeldata.animation[ANI_GRABWALKUP] && validanim(other, ANI_GRABBEDWALKUP))
27965             {
27966                 ent_set_anim(other, ANI_GRABBEDWALKUP, 0);
27967             }
27968             else if(self->animation == self->modeldata.animation[ANI_GRABWALKDOWN] && validanim(other, ANI_GRABBEDWALKDOWN))
27969             {
27970                 ent_set_anim(other, ANI_GRABBEDWALKDOWN, 0);
27971             }
27972             else if(self->animation == self->modeldata.animation[ANI_GRABBACKWALK] && validanim(other, ANI_GRABBEDBACKWALK))
27973             {
27974                 ent_set_anim(other, ANI_GRABBEDBACKWALK, 0);
27975             }
27976             else if(validanim(other, ANI_GRABBEDWALK))
27977             {
27978                 ent_set_anim(other, ANI_GRABBEDWALK, 0);
27979             }
27980             else if (validanim(other, ANI_GRABBED))
27981             {
27982                 ent_set_anim(other, ANI_GRABBED, 0);
27983             }
27984             else
27985             {
27986                 ent_set_anim(other, ANI_PAIN, 0);
27987             }
27988         }
27989         else
27990         {
27991             ent_set_anim(self, ANI_GRAB, 0);
27992             if (validanim(other, ANI_GRABBED))
27993             {
27994                 ent_set_anim(other, ANI_GRABBED, 0);
27995             }
27996             else
27997             {
27998                 ent_set_anim(other, ANI_PAIN, 0);
27999             }
28000         }
28001         // use check_link_move to set velocity, don't change it here
28002         other->velocity.z = other->velocity.x = 0;
28003         self->grabwalking = 1;
28004     }
28005 
28006     if(self->attacking)
28007     {
28008         self->releasetime = time + (GAME_SPEED / 2);    // reset releasetime when do collision
28009     }
28010 }
28011 
player_walkoff_check()28012 void player_walkoff_check()
28013 {
28014     if(self->modeldata.walkoffmovex & 1) //flip?
28015     {
28016         if((player[self->playerindex].keys & FLAG_MOVELEFT))
28017         {
28018             self->direction = DIRECTION_LEFT;
28019         }
28020         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
28021         {
28022             self->direction = DIRECTION_RIGHT;
28023         }
28024     }
28025     if(self->modeldata.walkoffmovex & 2) //move?
28026     {
28027         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
28028                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
28029         {
28030             self->velocity.x = -self->velocity.x;
28031         }
28032     }
28033     if(self->modeldata.walkoffmovex & 4) //Move x if vertical jump?
28034     {
28035         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
28036                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
28037         {
28038             self->velocity.x = -self->velocity.x;
28039         }
28040 
28041         if((player[self->playerindex].keys & FLAG_MOVELEFT) && (!self->velocity.x))
28042         {
28043             self->velocity.x -= self->modeldata.speed;
28044         }
28045         else if((player[self->playerindex].keys & FLAG_MOVERIGHT) && (!self->velocity.x))
28046         {
28047             self->velocity.x = self->modeldata.speed;
28048         }
28049     }
28050     if(self->modeldata.walkoffmovez & 2) //z move?
28051     {
28052         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
28053                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
28054         {
28055             self->velocity.z = -self->velocity.z;
28056         }
28057     }
28058     if(self->modeldata.walkoffmovez & 4) //Move z if vertical jump?
28059     {
28060         if((player[self->playerindex].keys & FLAG_MOVELEFT))
28061         {
28062             self->direction = DIRECTION_LEFT;
28063         }
28064         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
28065         {
28066             self->direction = DIRECTION_RIGHT;
28067         }
28068 
28069         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
28070                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
28071         {
28072             self->velocity.z = -self->velocity.z;
28073         }
28074 
28075         if((player[self->playerindex].keys & FLAG_MOVEUP) && (!self->velocity.z))
28076         {
28077             self->velocity.z -= (0.5 * self->modeldata.speed);
28078         }
28079         else if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (!self->velocity.z))
28080         {
28081             self->velocity.z = (0.5 * self->modeldata.speed);
28082         }
28083     }
28084 
28085     return;
28086 }
28087 
player_jump_check()28088 void player_jump_check()
28089 {
28090     int candospecial = 0;
28091 
28092     if(!noaircancel || !self->animating || self->animnum == self->jump.id)
28093     {
28094         //air special, copied and changed from Fugue's code
28095         if((!level->nospecial || level->nospecial == 3) && player[self->playerindex].playkeys & FLAG_SPECIAL)
28096         {
28097 
28098             if(validanim(self, ANI_JUMPSPECIAL))
28099             {
28100                 if(self->modeldata.animation[ANI_JUMPSPECIAL]->energycost && check_energy(COST_CHECK_MP, ANI_JUMPSPECIAL))
28101                 {
28102                     if(!healthcheat)
28103                     {
28104                         self->mp -= self->modeldata.animation[ANI_JUMPSPECIAL]->energycost->cost;
28105                     }
28106                     candospecial = 1;
28107                 }
28108                 else if(self->modeldata.animation[ANI_JUMPSPECIAL]->energycost && check_energy(COST_CHECK_HP, ANI_JUMPSPECIAL))
28109                 {
28110                     if(!healthcheat)
28111                     {
28112                         self->health -= self->modeldata.animation[ANI_JUMPSPECIAL]->energycost->cost;
28113                     }
28114                     candospecial = 1;
28115                 }
28116                 else if(validanim(self, ANI_JUMPCANT))
28117                 {
28118                     ent_set_anim(self, ANI_JUMPCANT, 0);
28119                     self->velocity.y = 0;
28120                 }
28121 
28122                 if(candospecial)
28123                 {
28124                     player[self->playerindex].playkeys &= ~FLAG_SPECIAL;
28125                     self->attacking = 1;
28126                     self->velocity.x = self->velocity.z = 0;                         // Kill movement when the special starts
28127                     self->velocity.y = 0;
28128                     ent_set_anim(self, ANI_JUMPSPECIAL, 0);
28129                 }
28130             }
28131         }//end of jumpspecial
28132 
28133         //jumpattacks, up down forward normal....we don't check energy cost
28134         else if(player[self->playerindex].playkeys & FLAG_ATTACK)
28135         {
28136             player[self->playerindex].playkeys &= ~FLAG_ATTACK;
28137             self->attacking = 1;
28138 
28139             if((player[self->playerindex].keys & FLAG_MOVEDOWN) && validanim(self, ANI_JUMPATTACK2))
28140             {
28141                 ent_set_anim(self, ANI_JUMPATTACK2, 0);
28142             }
28143             else if((player[self->playerindex].keys & FLAG_MOVEUP) && validanim(self, ANI_JUMPATTACK3))
28144             {
28145                 ent_set_anim(self, ANI_JUMPATTACK3, 0);
28146             }
28147             else if(self->running && validanim(self, ANI_RUNJUMPATTACK))
28148             {
28149                 ent_set_anim(self, ANI_RUNJUMPATTACK, 0);    // Added so an extra strong jump attack can be executed
28150             }
28151             else if(self->velocity.x != 0 && validanim(self, ANI_JUMPFORWARD))
28152             {
28153                 ent_set_anim(self, ANI_JUMPFORWARD, 0);    // If moving and set, do this attack
28154             }
28155             else if(validanim(self, ANI_JUMPATTACK))
28156             {
28157                 ent_set_anim(self, ANI_JUMPATTACK, 0);
28158             }
28159         }//end of jumpattack
28160     }
28161     if(self->modeldata.jumpmovex & 1) //flip?
28162     {
28163         if((player[self->playerindex].keys & FLAG_MOVELEFT))
28164         {
28165             self->direction = DIRECTION_LEFT;
28166         }
28167         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
28168         {
28169             self->direction = DIRECTION_RIGHT;
28170         }
28171     }
28172     if(self->modeldata.jumpmovex & 2) //move?
28173     {
28174         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
28175                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
28176         {
28177             self->velocity.x = -self->velocity.x;
28178         }
28179     }
28180     if(self->modeldata.jumpmovex & 4) //Move x if vertical jump?
28181     {
28182         if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) ||
28183                 ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0))
28184         {
28185             self->velocity.x = -self->velocity.x;
28186         }
28187 
28188         if((player[self->playerindex].keys & FLAG_MOVELEFT) && (!self->velocity.x))
28189         {
28190             self->velocity.x -= self->modeldata.speed;
28191         }
28192         else if((player[self->playerindex].keys & FLAG_MOVERIGHT) && (!self->velocity.x))
28193         {
28194             self->velocity.x = self->modeldata.speed;
28195         }
28196     }
28197     if(self->modeldata.jumpmovez & 2) //z move?
28198     {
28199         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
28200                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
28201         {
28202             self->velocity.z = -self->velocity.z;
28203         }
28204     }
28205     if(self->modeldata.jumpmovez & 4) //Move z if vertical jump?
28206     {
28207         if((player[self->playerindex].keys & FLAG_MOVELEFT))
28208         {
28209             self->direction = DIRECTION_LEFT;
28210         }
28211         else if((player[self->playerindex].keys & FLAG_MOVERIGHT))
28212         {
28213             self->direction = DIRECTION_RIGHT;
28214         }
28215 
28216         if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) ||
28217                 ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0))
28218         {
28219             self->velocity.z = -self->velocity.z;
28220         }
28221 
28222         if((player[self->playerindex].keys & FLAG_MOVEUP) && (!self->velocity.z))
28223         {
28224             self->velocity.z -= (0.5 * self->modeldata.speed);
28225         }
28226         else if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (!self->velocity.z))
28227         {
28228             self->velocity.z = (0.5 * self->modeldata.speed);
28229         }
28230     }
28231 
28232     return;
28233 }
28234 
player_pain_check()28235 void player_pain_check()
28236 {
28237     if(player_check_special())
28238     {
28239         self->inpain = 0;
28240         self->inbackpain = 0;
28241     }
28242 }
28243 
28244 // check riseattack input up+attack
player_lie_check()28245 void player_lie_check()
28246 {
28247     if(validanim(self, ANI_RISEATTACK) &&
28248             (player[self->playerindex].playkeys & FLAG_ATTACK) &&
28249             (player[self->playerindex].keys & FLAG_MOVEUP) &&
28250             (self->health > 0 && time > self->staydown.riseattack_stall))
28251     {
28252         player[self->playerindex].playkeys &= ~FLAG_ATTACK;
28253         if((player[self->playerindex].keys & FLAG_MOVELEFT))
28254         {
28255             self->direction = DIRECTION_LEFT;
28256         }
28257         if((player[self->playerindex].keys & FLAG_MOVERIGHT))
28258         {
28259             self->direction = DIRECTION_RIGHT;
28260         }
28261         self->stalltime = 0;
28262         set_riseattack(self, self->damagetype, 0);
28263     }
28264 }
28265 
player_charge_check()28266 void player_charge_check()
28267 {
28268     if(!((player[self->playerindex].keys & FLAG_JUMP) &&
28269             (player[self->playerindex].keys & FLAG_SPECIAL)))
28270     {
28271         self->takeaction = NULL;
28272         self->charging = 0;
28273         set_idle(self);
28274     }
28275 }
28276 
28277 
28278 // make a function so enemies can use
28279 // UT: jumphack is a temporary fix for jump cancel
check_costmove(int s,int fs,int jumphack)28280 int check_costmove(int s, int fs, int jumphack)
28281 {
28282     if(((fs == 1 && level->nospecial < 2) || (fs == 0 && level->nospecial == 0) || (fs == 0 && level->nospecial == 3)) &&
28283             (check_energy(COST_CHECK_HP, s) ||
28284              check_energy(COST_CHECK_MP, s))  )
28285     {
28286         if(!jumphack)
28287         {
28288             self->takeaction = common_attack_proc;
28289         }
28290         if(!nocost && !healthcheat)
28291         {
28292             if(self->modeldata.animation[s]->energycost)
28293             {
28294                 if(check_energy(COST_CHECK_MP, s))
28295                 {
28296                     self->mp -= self->modeldata.animation[s]->energycost->cost;
28297                 }
28298                 else
28299                 {
28300                     self->health -= self->modeldata.animation[s]->energycost->cost;
28301                 }
28302             }
28303         }
28304 
28305         self->velocity.x = self->velocity.z = 0;
28306         set_attacking(self);
28307         self->inpain = 0;
28308         self->inbackpain = 0;
28309         memset(self->combostep, 0, sizeof(*self->combostep) * 5);
28310         ent_unlink(self);
28311         ent_set_anim(self, s, 0);
28312         return 1;
28313     }
28314     return 0;
28315 }
28316 
match_combo(int a[],s_player * p,int l)28317 int match_combo(int a[], s_player *p, int l)
28318 {
28319     int j, step;
28320 
28321     for(j = 0; j < l; j++)
28322     {
28323         step = p->combostep - 1 - j;
28324         step = (step + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28325 
28326         // old: !(a[l - 1 - j]&p->combokey[step])
28327         if( ((a[l - 1 - j]&p->combokey[step]) ^ a[l - 1 - j]) ) // if input&combokey == 0 then not good btn
28328         {
28329             return 0;
28330         }
28331     }
28332 
28333     return 1;
28334 }
28335 
28336 
check_combo()28337 int check_combo()
28338 {
28339     int i, maxstep = -1, maxkeys = -1, valid = -1;
28340     s_com *com;
28341     s_player *p;
28342 
28343     p = player + self->playerindex;
28344 
28345     for(i = 0; i < self->modeldata.specials_loaded; i++)
28346     {
28347         com = self->modeldata.special + i;
28348 
28349         if(self->animation->cancel &&
28350                 (self->animnum != com->cancel ||
28351                  com->frame.min > self->animpos ||
28352                  com->frame.max < self->animpos ||
28353                  self->animation->animhits < com->hits))
28354         {
28355             continue;
28356         }
28357         else if(!self->animation->cancel &&
28358                 (com->cancel || !self->idling || diff(self->position.y, self->base) > 1) )
28359         {
28360             continue;
28361         }
28362 
28363         // find the longest possible combo with more keys pressed concurrently
28364         if( com->steps >= maxstep && com->numkeys > maxkeys &&
28365                 validanim(self, com->anim) &&
28366                 (check_energy(COST_CHECK_MP, com->anim) || check_energy(COST_CHECK_HP, com->anim)) &&
28367                 match_combo(com->input, p, com->steps))
28368         {
28369             // combo valid! but which better? The longest combo that has with more keys pressed concurrently
28370             valid = com->anim;
28371             maxstep = com->steps;
28372             maxkeys = com->numkeys;
28373         }
28374     }//end of for
28375 
28376     if(valid >= 0 && check_costmove(valid, 1, self->jumping))
28377     {
28378         return 1;
28379     }
28380 
28381     return 0;
28382 }
28383 
player_preinput()28384 int player_preinput()
28385 {
28386     if(player[self->playerindex].playkeys)
28387     {
28388         if(check_combo())
28389         {
28390             player[self->playerindex].playkeys &= ~FLAG_CONTROLKEYS;
28391             return 1;
28392         }
28393     }
28394     return 0;
28395 }
28396 
player_think()28397 void player_think()
28398 {
28399     int action = 0;		// 1=walking, 2=up, 3=down, 4=running
28400     int bkwalk = 0;   //backwalk
28401     int runx, runz, movex, movez;
28402     int t, t2;
28403     entity *other = NULL;
28404     float altdiff;
28405     int notinair;
28406 
28407     static int ll[] = {FLAG_MOVELEFT, FLAG_MOVELEFT};
28408     static int rr[] = {FLAG_MOVERIGHT, FLAG_MOVERIGHT};
28409     static int uu[] = {FLAG_MOVEUP, FLAG_MOVEUP};
28410     static int dd[] = {FLAG_MOVEDOWN, FLAG_MOVEDOWN};
28411     static int ba[] = {FLAG_BACKWARD, FLAG_ATTACK};
28412 
28413     int oldrunning = self->running;
28414     int pli = self->playerindex;
28415     s_player *pl = player + pli;
28416 
28417 
28418     if(pl->ent != self || self->dead)
28419     {
28420         return;
28421     }
28422 
28423     // check endlevel item
28424     if((other = find_ent_here(self, self->position.x, self->position.z, TYPE_ENDLEVEL, NULL)) && diff(self->position.y, other->position.y) <= 0.1)
28425     {
28426         if(!reached[0] && !reached[1] && !reached[2] && !reached[3])
28427         {
28428             addscore(pli, other->modeldata.score);
28429         }
28430         reached[pli] = 1;
28431 
28432         if (!other->modeldata.subtype || (other->modeldata.subtype == SUBTYPE_BOTH &&
28433                                           (reached[0] + reached[1] + reached[2] + reached[3]) >= (count_ents(TYPE_PLAYER))))
28434         {
28435             level_completed = 1;
28436 
28437             if(other->modeldata.branch)
28438             {
28439                 strncpy( branch_name, other->modeldata.branch, MAX_NAME_LEN);    //now, you can branch to another level
28440             }
28441             return;
28442         }
28443     }
28444 
28445     if(time > self->rush.time)
28446     {
28447         self->rush.count.current = 0;
28448         self->rush.time = 0;
28449     }
28450 
28451     if(player_preinput())
28452     {
28453         goto endthinkcheck;
28454     }
28455 
28456     if(self->charging)
28457     {
28458         player_charge_check();
28459         goto endthinkcheck;
28460     }
28461 
28462     if(self->inpain || (self->link && !self->grabbing))
28463     {
28464         player_pain_check();
28465         goto endthinkcheck;
28466     }
28467 
28468     // falling? check for landing
28469     if(self->projectile == 2)
28470     {
28471         player_fall_check();
28472         goto endthinkcheck;
28473     }
28474 
28475     // grab section, dont move if still animating
28476     if(self->grabbing && !self->attacking && self->takeaction != common_throw_wait)
28477     {
28478         player_grab_check();
28479         goto endthinkcheck;
28480     }
28481 
28482     // jump section
28483     if(self->jumping)
28484     {
28485         player_jump_check();
28486         goto endthinkcheck;
28487     }
28488 
28489     if(self->animnum == ANI_WALKOFF)
28490     {
28491         player_walkoff_check();
28492         goto endthinkcheck;
28493     }
28494 
28495     if(self->drop && self->position.y == self->base && !self->velocity.y)
28496     {
28497         player_lie_check();
28498         goto endthinkcheck;
28499     }
28500 
28501 
28502     // cant do anything if busy
28503     if(!self->idling && !(self->animation->idle && self->animation->idle[self->animpos]))
28504     {
28505         goto endthinkcheck;
28506     }
28507 
28508 
28509     // Check if entity is under a platform
28510     /*if(self->modeldata.subject_to_platform > 0 && (heightvar = self->animation->size.y ? self->animation->size.y : self->modeldata.size.y) &&
28511             validanim(self, ANI_DUCK) && check_platform_between(self->position.x, self->position.z, self->position.y, self->position.y + heightvar, self))
28512     {
28513         self->idling = 0;
28514         self->takeaction = common_stuck_underneath;
28515         ent_set_anim(self, ANI_DUCK, 0);
28516         goto endthinkcheck;
28517     }*/
28518 
28519     altdiff = diff(self->position.y, self->base);
28520     notinair = (self->landed_on_platform ? altdiff < 5 : altdiff < 2);
28521 
28522     if(pl->playkeys & FLAG_MOVEUP)
28523     {
28524         t = (notinair && match_combo(uu, pl, 2));
28525         if(t && (self->modeldata.runupdown & 2) && validanim(self, ANI_RUN))
28526         {
28527             pl->playkeys &= ~FLAG_MOVEUP;
28528             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28529             self->running = 1;    // Player begins to run
28530         }
28531         else if(t && validanim(self, ANI_ATTACKUP))
28532         {
28533             // New u u combo attack
28534             pl->playkeys &= ~FLAG_MOVEUP;
28535             self->takeaction = common_attack_proc;
28536             set_attacking(self);
28537             self->combostep[0] = 0;
28538             self->velocity.x = self->velocity.z = 0;
28539             ent_set_anim(self, ANI_ATTACKUP, 0);
28540             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS; // this workaround deals default freespecial2
28541             goto endthinkcheck;
28542         }
28543         else if(t && validanim(self, ANI_DODGE))
28544         {
28545             // New dodge move like on SOR3
28546             pl->playkeys &= ~FLAG_MOVEUP;
28547             self->takeaction = common_dodge;
28548             self->combostep[0] = 0;
28549             self->idling = 0;
28550             self->velocity.z = -self->modeldata.speed * 1.75;
28551             self->velocity.x = 0;// OK you can use jumpframe to modify this anyway
28552             ent_set_anim(self, ANI_DODGE, 0);
28553             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28554             goto endthinkcheck;
28555         }
28556     }
28557 
28558     if(pl->playkeys & FLAG_MOVEDOWN)
28559     {
28560         t = (notinair && match_combo(dd, pl, 2));
28561         if(t && (self->modeldata.runupdown & 2) && validanim(self, ANI_RUN))
28562         {
28563             pl->playkeys &= ~FLAG_MOVEDOWN;
28564             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28565             self->running = 1;    // Player begins to run
28566         }
28567         else if(t && validanim(self, ANI_ATTACKDOWN))
28568         {
28569             // New d d combo attack
28570             pl->playkeys &= ~FLAG_MOVEDOWN;
28571             self->takeaction = common_attack_proc;
28572             set_attacking(self);
28573             self->velocity.x = self->velocity.z = 0;
28574             self->combostep[0] = 0;
28575             ent_set_anim(self, ANI_ATTACKDOWN, 0);
28576             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28577             goto endthinkcheck;
28578         }
28579         else if(t && validanim(self, ANI_DODGE))
28580         {
28581             // New dodge move like on SOR3
28582             pl->playkeys &= ~FLAG_MOVEDOWN;
28583             self->takeaction = common_dodge;
28584             self->combostep[0] = 0;
28585             self->idling = 0;
28586             self->velocity.z = self->modeldata.speed * 1.75;
28587             self->velocity.x = 0;
28588             ent_set_anim(self, ANI_DODGE, 0);
28589             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28590             goto endthinkcheck;
28591         }
28592     }
28593 
28594     if((pl->playkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)))
28595     {
28596         int t3;
28597 
28598         t = (notinair && ((self->direction == DIRECTION_RIGHT && match_combo(rr, pl, 2)) || (self->direction == DIRECTION_LEFT && match_combo(ll, pl, 2))));
28599         t3 = (notinair && self->modeldata.facing && ((self->direction == DIRECTION_RIGHT && match_combo(ll, pl, 2)) || (self->direction == DIRECTION_LEFT && match_combo(rr, pl, 2))));
28600 
28601         if(t && validanim(self, ANI_RUN))
28602         {
28603             pl->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both
28604             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28605             self->running = 1;    // Player begins to run
28606         }
28607         else if(t3 && validanim(self, ANI_BACKRUN))
28608         {
28609             pl->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both
28610             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28611             self->running = 1;    // Player begins to run
28612         }
28613         else if(t && validanim(self, ANI_ATTACKFORWARD))
28614         {
28615             pl->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT);
28616             self->takeaction = common_attack_proc;
28617             set_attacking(self);
28618             self->velocity.x = self->velocity.z = 0;
28619             self->combostep[0] = 0;
28620             ent_set_anim(self, ANI_ATTACKFORWARD, 0);
28621             pl->combostep = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28622             goto endthinkcheck;
28623         }
28624     }
28625 
28626     if(!ajspecial && (pl->playkeys & FLAG_JUMP) && validanim(self, ANI_ATTACKBOTH))
28627     {
28628         if((pl->keys & FLAG_ATTACK) && notinair)
28629         {
28630             pl->playkeys &= ~FLAG_JUMP;
28631             self->takeaction = common_attack_proc;
28632             set_attacking(self);
28633             self->velocity.x = self->velocity.z = 0;
28634             self->combostep[0] = 0;
28635             self->stalltime = 0;    // If attack is pressed, holding down attack to execute attack3 is no longer valid
28636             ent_set_anim(self, ANI_ATTACKBOTH, 0);
28637             goto endthinkcheck;
28638         }
28639     }
28640 
28641     if((pl->playkeys & FLAG_JUMP) &&  validanim(self, ANI_CHARGE))
28642     {
28643         if((pl->keys & FLAG_SPECIAL) && notinair)
28644         {
28645             pl->playkeys &= ~FLAG_JUMP;
28646             self->takeaction = common_charge;
28647             self->combostep[0] = 0;
28648             self->velocity.x = self->velocity.z = 0;
28649             self->stalltime = 0;
28650             set_charging(self);
28651             ent_set_anim(self, ANI_CHARGE, 0);
28652             goto endthinkcheck;
28653         }
28654     }
28655 
28656     if(pl->playkeys & FLAG_SPECIAL )    //    The special button can now be used for freespecials
28657     {
28658         if( validanim(self, ANI_SPECIAL2) && notinair &&
28659                 (self->direction == DIRECTION_LEFT ?
28660                  (pl->keys & FLAG_MOVELEFT) :
28661                  (pl->keys & FLAG_MOVERIGHT))  )
28662         {
28663             if(check_costmove(ANI_SPECIAL2, 0, 0))
28664             {
28665                 pl->playkeys &= ~FLAG_SPECIAL;
28666                 goto endthinkcheck;
28667             }
28668         }
28669 
28670         if(validanim(self, ANI_BLOCK) && notinair)   // New block code for players
28671         {
28672             pl->playkeys &= ~FLAG_SPECIAL;
28673             self->takeaction = common_block;
28674             self->velocity.x = self->velocity.z = 0;
28675             set_blocking(self);
28676             self->combostep[0] = 0;
28677             ent_set_anim(self, ANI_BLOCK, 0);
28678             goto endthinkcheck;
28679         }
28680     }
28681 
28682     if(notinair && player_check_special())
28683     {
28684         goto endthinkcheck;    // So you don't perform specials falling off the edge
28685     }
28686 
28687     if((pl->releasekeys & FLAG_ATTACK))
28688     {
28689         if(self->stalltime && notinair &&
28690                 ((validanim(self, ANI_CHARGEATTACK) && self->stalltime + (GAME_SPEED * self->modeldata.animation[ANI_CHARGEATTACK]->chargetime) < time) ||
28691                  (!validanim(self, ANI_CHARGEATTACK) && validanim(self, animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1])
28692                   && self->modeldata.chainlength > 0 && self->stalltime + (GAME_SPEED * self->modeldata.animation[animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1]]->chargetime) < time)))
28693         {
28694             self->takeaction = common_attack_proc;
28695             set_attacking(self);
28696             self->velocity.x = self->velocity.z = 0;
28697 
28698             self->stalltime = 0;
28699             self->combostep[0] = 0;
28700 
28701             if(SAMPLE_PUNCH >= 0)
28702             {
28703                 sound_play_sample(SAMPLE_PUNCH, 0, savedata.effectvol, savedata.effectvol, 100);
28704             }
28705 
28706             if(validanim(self, ANI_CHARGEATTACK))
28707             {
28708                 ent_set_anim(self, ANI_CHARGEATTACK, 0);
28709             }
28710             else if(validanim(self, animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1]))
28711             {
28712                 ent_set_anim(self, animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1], 0);
28713             }
28714             goto endthinkcheck;
28715         }
28716         self->stalltime = 0;
28717     }
28718 
28719     if((pl->playkeys & FLAG_ATTACK)  && notinair)
28720     {
28721         pl->playkeys &= ~FLAG_ATTACK;
28722         self->stalltime = 0;    // Disable the attack3 stalltime
28723 
28724         if(pl->keys & FLAG_MOVEDOWN && validanim(self, ANI_DUCKATTACK) && PLAYER_MIN_Z == PLAYER_MAX_Z)
28725         {
28726             self->takeaction = common_attack_proc;
28727             set_attacking(self);
28728             self->velocity.x = self->velocity.z = 0;
28729             self->combostep[0] = 0;
28730             ent_set_anim(self, ANI_DUCKATTACK, 0);
28731             goto endthinkcheck;
28732         }
28733 
28734         if(self->running && validanim(self, ANI_RUNATTACK))   // New run attack code section
28735         {
28736             self->takeaction = common_attack_proc;
28737             set_attacking(self);
28738             self->velocity.x = self->velocity.z = 0;
28739             self->combostep[0] = 0;
28740             self->running = 0;
28741             ent_set_anim(self, ANI_RUNATTACK, 0);
28742             goto endthinkcheck;
28743         }
28744 
28745         if(validanim(self, ANI_ATTACKBACKWARD) && match_combo(ba, pl, 2))
28746         {
28747             t = (pl->combostep - 1 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28748             t2 = (pl->combostep - 2 + MAX_SPECIAL_INPUTS) % MAX_SPECIAL_INPUTS;
28749             if(pl->inputtime[t] - pl->inputtime[t2] < GAME_SPEED / 10)
28750             {
28751                 self->takeaction = common_attack_proc;
28752                 set_attacking(self);
28753                 self->velocity.x = self->velocity.z = 0;
28754                 if(self->direction == DIRECTION_LEFT && (pl->combokey[t2]&FLAG_MOVELEFT))
28755                 {
28756                     self->direction = DIRECTION_RIGHT;
28757                 }
28758                 else if(self->direction == DIRECTION_RIGHT && (pl->combokey[t2]&FLAG_MOVERIGHT))
28759                 {
28760                     self->direction = DIRECTION_LEFT;
28761                 }
28762                 self->combostep[0] = 0;
28763                 ent_set_anim(self, ANI_ATTACKBACKWARD, 0);
28764                 goto endthinkcheck;
28765             }
28766         }
28767 
28768         if( validanim(self, ANI_GET) && (other = find_ent_here(self, self->position.x, self->position.z, TYPE_ITEM, player_test_pickable)) )
28769         {
28770             self->velocity.x = self->velocity.z = 0;
28771             set_getting(self);
28772             self->takeaction = common_get;
28773             ent_set_anim(self, ANI_GET, 0);
28774             execute_didhit_script(other, self, 0, 0, other->modeldata.subtype, 0, 0, 0, 0, 0, 0); //Execute didhit script as if item "hit" collecter to allow easy item scripting.
28775             didfind_item(other);
28776             goto endthinkcheck;
28777         }
28778 
28779         // Use stalltime to charge end-move
28780         self->stalltime = time;
28781         self->velocity.x = self->velocity.z = 0;
28782 
28783         if( self->weapent &&
28784                 self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
28785                 validanim(self, ANI_THROWATTACK)  )
28786         {
28787             self->takeaction = common_attack_proc;
28788             set_attacking(self);
28789             ent_set_anim(self, ANI_THROWATTACK, 0);
28790             goto endthinkcheck;
28791         }
28792         else if(perform_atchain())
28793         {
28794             if(SAMPLE_PUNCH >= 0 && self->attacking)
28795             {
28796                 sound_play_sample(SAMPLE_PUNCH, 0, savedata.effectvol, savedata.effectvol, 100);
28797             }
28798             goto endthinkcheck;
28799         }
28800 
28801     }
28802     // 7-1-2005 spawn projectile end
28803 
28804     if(pl->playkeys & FLAG_JUMP  && notinair)
28805     {
28806         // Added !inair(self) so players can't jump when falling into holes
28807         pl->playkeys &= ~FLAG_JUMP;
28808 
28809         if(self->running)
28810         {
28811             //Slide
28812             if((pl->keys & FLAG_MOVEDOWN) && validanim(self, ANI_RUNSLIDE))
28813             {
28814                 self->takeaction = common_attack_proc;
28815                 set_attacking(self);
28816                 self->velocity.x = self->velocity.z = 0;
28817                 self->combostep[0] = 0;
28818                 self->running = 0;
28819                 ent_set_anim(self, ANI_RUNSLIDE, 0);
28820                 goto endthinkcheck;
28821             }
28822 
28823             if(validanim(self, ANI_RUNJUMP))
28824             {
28825                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_RUNJUMP);
28826             }
28827             else if(validanim(self, ANI_FORWARDJUMP))
28828             {
28829                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
28830             }
28831             else if(validanim(self, ANI_JUMP))
28832             {
28833                 tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed * self->modeldata.runjumpdist, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
28834             }
28835         }
28836         else
28837         {
28838             //Slide
28839             if((pl->keys & FLAG_MOVEDOWN) && validanim(self, ANI_SLIDE))
28840             {
28841                 self->takeaction = common_attack_proc;
28842                 set_attacking(self);
28843                 self->velocity.x = self->velocity.z = 0;
28844                 self->combostep[0] = 0;
28845                 self->running = 0;
28846                 ent_set_anim(self, ANI_SLIDE, 0);
28847                 goto endthinkcheck;
28848             }
28849 
28850             if(!(pl->keys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)) && validanim(self, ANI_JUMP))
28851             {
28852                 tryjump(self->modeldata.jumpheight, 0, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
28853                 goto endthinkcheck;
28854             }
28855             else if((pl->keys & FLAG_MOVELEFT))
28856             {
28857                 self->direction = DIRECTION_LEFT;
28858             }
28859             else if((pl->keys & FLAG_MOVERIGHT))
28860             {
28861                 self->direction = DIRECTION_RIGHT;
28862             }
28863 
28864             if(validanim(self, ANI_FORWARDJUMP))
28865             {
28866                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_FORWARDJUMP);
28867             }
28868             else if(validanim(self, ANI_JUMP))
28869             {
28870                 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, (self->modeldata.jumpmovez) ? self->velocity.z : 0, ANI_JUMP);
28871             }
28872         }
28873         return;
28874     }
28875 
28876     //dang long run checking logic
28877     if(self->running)
28878     {
28879         runx = runz = movex = movez = 0;
28880         if(pl->keys & FLAG_MOVEUP)
28881         {
28882             movez--;
28883         }
28884         if(pl->keys & FLAG_MOVEDOWN)
28885         {
28886             movez++;
28887         }
28888         if(pl->keys & FLAG_MOVELEFT)
28889         {
28890             movex--;
28891         }
28892         if(pl->keys & FLAG_MOVERIGHT)
28893         {
28894             movex++;
28895         }
28896         if(oldrunning)
28897         {
28898             if(self->velocity.z < 0)
28899             {
28900                 runz--;
28901             }
28902             else if(self->velocity.z > 0)
28903             {
28904                 runz++;
28905             }
28906             if(self->velocity.x < 0)
28907             {
28908                 runx--;
28909             }
28910             else if(self->velocity.x > 0)
28911             {
28912                 runx++;
28913             }
28914         }
28915         if(!self->modeldata.runupdown)
28916         {
28917             if(movez || !movex)
28918             {
28919                 self->running = 0;
28920             }
28921         }
28922         else if(self->modeldata.runupdown & 4)
28923         {
28924             if(!movex && !movez)
28925             {
28926                 self->running = 0;
28927             }
28928             else if(movex && !movez && runx == -movex)
28929             {
28930                 self->running = 0;
28931             }
28932             else if(movez && !movex && runz == -movez)
28933             {
28934                 self->running = 0;
28935             }
28936             else if(movex && movez && diff(movex, runx) + diff(movez, runz) > 2)
28937             {
28938                 self->running = 0;
28939             }
28940         }
28941         else if(self->modeldata.runupdown)
28942         {
28943             if(!movex || movex == -runx)
28944             {
28945                 self->running = 0;
28946             }
28947         }
28948 
28949     }
28950 
28951     if(PLAYER_MIN_Z != PLAYER_MAX_Z)
28952     {
28953         // More of a platform feel
28954         if(pl->keys & FLAG_MOVEUP)
28955         {
28956             //if(!self->modeldata.runupdown ) self->running = 0;    // Quits running if player presses up (or the up animation exists
28957 
28958             if(validanim(self, ANI_UP) && !self->running)
28959             {
28960                 action = 2;
28961                 self->velocity.z = -self->modeldata.speed / 2;  // Used for up animation
28962             }
28963             else if(self->running)
28964             {
28965                 action = 4;
28966                 self->velocity.z = -self->modeldata.runspeed / 2;  // Moves up at a faster rate running
28967             }
28968             else
28969             {
28970                 action = 1;
28971                 self->velocity.z = -self->modeldata.speed / 2;
28972             }
28973         }
28974         else if(pl->keys & FLAG_MOVEDOWN)
28975         {
28976             //if(!self->modeldata.runupdown ) self->running = 0;    // Quits running if player presses down (or the down animation exists
28977 
28978             if(validanim(self, ANI_DOWN) && !self->running )
28979             {
28980                 action = 3;
28981                 self->velocity.z = self->modeldata.speed / 2;  // Used for down animation
28982             }
28983             else if(self->running)
28984             {
28985                 action = 4;
28986                 self->velocity.z = self->modeldata.runspeed / 2;  // Moves down at a faster rate running
28987             }
28988             else
28989             {
28990                 action = 1;
28991                 self->velocity.z = self->modeldata.speed / 2;
28992             }
28993         }
28994         else if(!(pl->keys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
28995         {
28996             self->velocity.z = 0;
28997         }
28998     }
28999     else if(validanim(self, ANI_DUCK) && pl->keys & FLAG_MOVEDOWN  && notinair)
29000     {
29001         ent_set_anim(self, ANI_DUCK, 0);
29002         self->velocity.x = self->velocity.z = 0;
29003         goto endthinkcheck;
29004     }
29005 
29006     if(pl->keys & FLAG_MOVELEFT)
29007     {
29008         if(self->direction == DIRECTION_RIGHT)
29009         {
29010             //self->running = 0;    // Quits running if player changes direction
29011             if(self->modeldata.turndelay && !self->turntime)
29012             {
29013                 self->turntime = time + self->modeldata.turndelay;
29014             }
29015             else if(self->turntime && time >= self->turntime)
29016             {
29017                 self->turntime = 0;
29018                 if(validanim(self, ANI_TURN))
29019                 {
29020                     self->takeaction = common_turn;
29021                     set_turning(self);
29022                     self->velocity.x = self->velocity.z = 0;
29023                     ent_set_anim(self, ANI_TURN, 0);
29024                     goto endthinkcheck;
29025                 }
29026                 self->direction = DIRECTION_LEFT;
29027             }
29028             else if(!self->modeldata.turndelay && validanim(self, ANI_TURN))
29029             {
29030                 self->takeaction = common_turn;
29031                 set_turning(self);
29032                 self->velocity.x = self->velocity.z = 0;
29033                 ent_set_anim(self, ANI_TURN, 0);
29034                 goto endthinkcheck;
29035             }
29036             else if(!self->turntime)
29037             {
29038                 self->direction = DIRECTION_LEFT;
29039             }
29040         }
29041         else
29042         {
29043             self->turntime = 0;
29044         }
29045 
29046         if(self->running)
29047         {
29048             action = 4;
29049             self->velocity.x = -self->modeldata.runspeed;    // If running, player moves at a faster rate
29050         }
29051         else if(action != 2 && action != 3)
29052         {
29053             action = 1;
29054             self->velocity.x = -self->modeldata.speed;
29055         }
29056         else
29057         {
29058             self->velocity.x = -self->modeldata.speed;
29059         }
29060     }
29061     else if(pl->keys & FLAG_MOVERIGHT)
29062     {
29063         if(self->direction == DIRECTION_LEFT)
29064         {
29065             //self->running = 0;    // Quits running if player changes direction
29066             if(self->modeldata.turndelay && !self->turntime)
29067             {
29068                 self->turntime = time + self->modeldata.turndelay;
29069             }
29070             else if(self->turntime && time >= self->turntime)
29071             {
29072                 self->turntime = 0;
29073                 if(validanim(self, ANI_TURN))
29074                 {
29075                     self->takeaction = common_turn;
29076                     set_turning(self);
29077                     self->velocity.x = self->velocity.z = 0;
29078                     ent_set_anim(self, ANI_TURN, 0);
29079                     goto endthinkcheck;
29080                 }
29081                 self->direction = DIRECTION_RIGHT;
29082             }
29083             else if(!self->modeldata.turndelay && validanim(self, ANI_TURN))
29084             {
29085                 self->takeaction = common_turn;
29086                 set_turning(self);
29087                 self->velocity.x = self->velocity.z = 0;
29088                 ent_set_anim(self, ANI_TURN, 0);
29089                 goto endthinkcheck;
29090             }
29091             else if(!self->turntime)
29092             {
29093                 self->direction = DIRECTION_RIGHT;
29094             }
29095         }
29096         else
29097         {
29098             self->turntime = 0;
29099         }
29100 
29101         if(self->running)
29102         {
29103             action = 4;
29104             self->velocity.x = self->modeldata.runspeed;    // If running, player moves at a faster rate
29105         }
29106         else if(action != 2 && action != 3)
29107         {
29108             action = 1;
29109             self->velocity.x = self->modeldata.speed;
29110         }
29111         else
29112         {
29113             self->velocity.x = self->modeldata.speed;
29114         }
29115     }
29116     else if(!((pl->keys & FLAG_MOVELEFT) ||
29117               (pl->keys & FLAG_MOVERIGHT)) )
29118     {
29119         //self->running = 0;    // Player let go of left/right and so quits running
29120         self->velocity.x = 0;
29121         self->turntime = 0;
29122     }
29123 
29124     if((other = find_ent_here(self, self->position.x, self->position.z, TYPE_ITEM, player_test_touch))  )
29125     {
29126         execute_didhit_script(other, self, 0, 0, other->modeldata.subtype, 0, 0, 0, 0, 0, 0); //Execute didhit script as if item "hit" collecter to allow easy item scripting.
29127         didfind_item(other);    // Added function to clean code up a bit
29128     }
29129 
29130     if(action)
29131     {
29132         self->takeaction = NULL;
29133         self->idling = 1;
29134     }
29135     switch(action)
29136     {
29137     case 1:
29138         // back walk feature
29139         if(level && validanim(self, ANI_BACKWALK))
29140         {
29141             if(self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT)
29142             {
29143                 bkwalk = !self->direction;
29144             }
29145             else if(self->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_RIGHT)
29146             {
29147                 bkwalk = self->direction;
29148             }
29149             else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT) && self->direction == DIRECTION_LEFT)
29150             {
29151                 bkwalk = 1;
29152             }
29153             else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT) && self->direction == DIRECTION_RIGHT)
29154             {
29155                 bkwalk = 1;
29156             }
29157             else if(self->turntime && self->modeldata.turndelay)
29158             {
29159                 bkwalk = 1;
29160             }
29161             if(bkwalk)
29162             {
29163                 common_backwalk_anim(self);    //ent_set_anim(self, ANI_BACKWALK, 0);
29164             }
29165             else
29166             {
29167                 common_walk_anim(self);    //ent_set_anim(self, ANI_WALK, 0);    // If neither up nor down exist, set to walk
29168             }
29169         }
29170         else
29171         {
29172             common_walk_anim(self);    //ent_set_anim(self, ANI_WALK, 0);    // If neither up nor down exist, set to walk
29173         }
29174         break;
29175     case 2:
29176         common_up_anim(self); //ent_set_anim(self, ANI_UP, 0);    // Set to up animation if exists
29177         break;
29178     case 3:
29179         common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0);    // Set to down animation if exists
29180         break;
29181     case 4:
29182         if (validanim(self, ANI_BACKRUN))
29183         {
29184             if (is_in_backrun(self)) ent_set_anim(self, ANI_BACKRUN, 0);
29185             else ent_set_anim(self, ANI_RUN, 0);
29186         }
29187         else ent_set_anim(self, ANI_RUN, 0); // Set to run animation if exists
29188 
29189         break;
29190     default:
29191         if(self->idling)
29192         {
29193             common_idle_anim(self);
29194         }
29195         break;
29196     }
29197 
29198 
29199 endthinkcheck:
29200     //insert check here
29201     return;
29202 }
29203 
is_in_backrun(entity * self)29204 int is_in_backrun(entity* self)
29205 {
29206     if ( ((self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT) && self->velocity.x < 0) ||
29207          ((self->modeldata.facing == FACING_ADJUST_LEFT  || level->facing == FACING_ADJUST_LEFT)  && self->velocity.x > 0) ||
29208          (((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT)) && self->velocity.x > 0) ||
29209          (((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT))  && self->velocity.x > 0)
29210        ) return 1;
29211     else return 0;
29212 }
29213 
29214 //ammo count goes down
subtract_shot()29215 void subtract_shot()
29216 {
29217     if(self->weapent && self->weapent->modeldata.shootnum)
29218     {
29219         self->weapent->modeldata.shootnum--;
29220         if(!self->weapent->modeldata.shootnum)
29221         {
29222             self->weapent->modeldata.counter = 0;
29223             dropweapon(0);
29224         }
29225     }
29226 
29227 }
29228 
29229 
dropweapon(int flag)29230 void dropweapon(int flag)
29231 {
29232     int wall;
29233     entity *other = NULL;
29234 
29235     if(self->weapent)
29236     {
29237         if(self->weapent->modeldata.typeshot || (!self->weapent->modeldata.typeshot && self->weapent->modeldata.shootnum))
29238         {
29239             self->weapent->direction = self->direction;//same direction as players, 2007 -2 - 11   by UTunnels
29240             if(flag < 2)
29241             {
29242                 self->weapent->modeldata.counter -= flag;
29243             }
29244             self->weapent->position.z = self->position.z;
29245             self->weapent->position.x = self->position.x;
29246             self->weapent->position.y = self->position.y;
29247 
29248             other = check_platform(self->weapent->position.x, self->weapent->position.z, self);
29249             wall = checkwall(self->weapent->position.x, self->weapent->position.z);
29250 
29251             if(other && other != self->weapent)
29252             {
29253                 self->weapent->base += other->position.y + other->animation->platform[other->animpos][7];
29254             }
29255             else if(wall >= 0)
29256             {
29257                 self->weapent->base += level->walls[wall].height;
29258             }
29259 
29260             if(validanim(self->weapent, ANI_RESPAWN))
29261             {
29262                 ent_set_anim(self->weapent, ANI_RESPAWN, 1);
29263             }
29264             else if(validanim(self->weapent, ANI_SPAWN))
29265             {
29266                 ent_set_anim(self->weapent, ANI_SPAWN, 1);
29267             }
29268             else
29269             {
29270                 if(validanim(self->weapent, ANI_IDLE)) ent_set_anim(self->weapent, ANI_IDLE, 1);
29271             }
29272 
29273             if(!self->weapent->modeldata.counter)
29274             {
29275                 if(!self->modeldata.animal)
29276                 {
29277                     self->weapent->blink = 1;
29278                     self->weapent->takeaction = common_lie;
29279                 }
29280                 else
29281                 {
29282                     self->weapent->modeldata.type = TYPE_NONE;
29283                     self->weapent->think = runanimal;
29284                 }
29285             }
29286             self->weapent->nextthink = time + 1;
29287         }
29288         self->weapent = NULL;
29289     }
29290     if(flag < 2)
29291     {
29292         if(self->modeldata.type & TYPE_PLAYER)
29293         {
29294             if(player[self->playerindex].weapnum)
29295             {
29296                 set_weapon(self, player[self->playerindex].weapnum, 0);
29297             }
29298             else
29299             {
29300                 set_weapon(self, level->setweap, 0);
29301             }
29302         }
29303         else
29304         {
29305             set_weapon(self, 0, 0);
29306         }
29307     }
29308 
29309     if(self->modeldata.weaploss[1] > 0)
29310     {
29311         set_weapon(self, self->modeldata.weaploss[1], 0);
29312     }
29313 }
29314 
29315 
player_takedamage(entity * other,s_collision_attack * attack)29316 int player_takedamage(entity *other, s_collision_attack *attack)
29317 {
29318     s_collision_attack atk = *attack;
29319     //printf("damaged by: '%s' %d\n", other->name, attack->attack_force);
29320     if(healthcheat || (level->nohurt && (other->modeldata.type & TYPE_ENEMY)))
29321     {
29322         atk.attack_force = 0;
29323     }
29324 
29325     return common_takedamage(other, &atk);
29326 }
29327 
29328 
29329 ////////////////////////////////
29330 
29331 // Called when player re-enters the game.
29332 // Drop all enemies EXCEPT for the linked/frozen ones.
drop_all_enemies()29333 void drop_all_enemies()
29334 {
29335     int i;
29336     entity *weapself = self;
29337     for(i = 0; i < ent_max; i++)
29338     {
29339         if(ent_list[i]->exists &&
29340                 ent_list[i]->health > 0 &&
29341                 (ent_list[i]->modeldata.type & TYPE_ENEMY) &&
29342                 !ent_list[i]->owner &&    // Don't want to knock down a projectile
29343                 !ent_list[i]->frozen &&    // Don't want to unfreeze a frozen enemy
29344                 !ent_list[i]->modeldata.nomove &&
29345                 !ent_list[i]->modeldata.nodrop &&
29346                 validanim(ent_list[i], ANI_FALL) )
29347         {
29348             ent_list[i]->attacking = 0;
29349             ent_list[i]->projectile = 0;
29350             ent_list[i]->takeaction = common_fall;//enemy_fall;
29351             ent_list[i]->damage_on_landing = 0;
29352             self = ent_list[i];
29353             ent_unlink(self);
29354             ent_list[i]->velocity.x = (self->direction == DIRECTION_RIGHT) ? (-1.2) : 1.2;
29355             dropweapon(1);
29356             toss(ent_list[i], 2.5 + randf(1));
29357             ent_list[i]->knockdowncount = ent_list[i]->modeldata.knockdowncount;
29358 
29359             ent_list[i]->knockdowntime = 0;
29360             set_fall(ent_list[i], ATK_NORMAL, 1, self, 0, 0, 0, 0, 0, 0, 0);
29361         }
29362     }
29363     self = weapself;
29364 }
29365 
29366 
29367 
29368 // Called when boss dies
kill_all_enemies()29369 void kill_all_enemies()
29370 {
29371     int i;
29372     s_collision_attack attack;
29373     entity *tmpself = NULL;
29374 
29375     attack = emptyattack;
29376     attack.attack_type = max_attack_types;
29377     attack.dropv.y = default_model_dropv.y;
29378     attack.dropv.x = default_model_dropv.x;
29379     attack.dropv.z = default_model_dropv.z;
29380 
29381     tmpself = self;
29382     for(i = 0; i < ent_max; i++)
29383     {
29384         if(  ent_list[i]->exists
29385                 && ent_list[i]->health > 0
29386                 && (ent_list[i]->modeldata.type & TYPE_ENEMY)
29387                 && ent_list[i]->takedamage)
29388         {
29389             self = ent_list[i];
29390             attack.attack_force = self->health;
29391             self->takedamage(tmpself, &attack);
29392             self->dead = 1;
29393         }
29394     }
29395     self = tmpself;
29396 }
29397 
29398 
29399 
smart_bomb(entity * e,s_collision_attack * attack)29400 void smart_bomb(entity *e, s_collision_attack *attack)    // New method for smartbombs
29401 {
29402     int i, hostile, hit = 0;
29403     entity *tmpself = NULL;
29404 
29405     hostile = e->modeldata.hostile;
29406     if(e->modeldata.type & TYPE_PLAYER)
29407     {
29408         hostile &= ~(TYPE_PLAYER);
29409     }
29410 
29411     tmpself = self;
29412     for(i = 0; i < ent_max; i++)
29413     {
29414         if( ent_list[i]->exists
29415                 && ent_list[i] != e
29416                 && ent_list[i]->health > 0
29417                 && (ent_list[i]->modeldata.type & (e->modeldata.hostile)))
29418         {
29419             self = ent_list[i];
29420             hit = 1; // for nocost, if the bomb doesn't hit, it won't cost energy
29421             if(self->takedamage)
29422             {
29423                 //attack.attack_drop = self->modeldata.knockdowncount+1;
29424                 self->takedamage(e, attack);
29425             }
29426             else
29427             {
29428                 self->health -= attack->attack_force;
29429                 if(self->health <= 0)
29430                 {
29431                     kill(self);
29432                 }
29433             }
29434         }
29435     }
29436     if(nocost && hit && smartbomber) // don't use e, because this can be an item-bomb
29437     {
29438         self = smartbomber;
29439 
29440         // Energycost defined?
29441         if(self->modeldata.animation[ANI_SPECIAL]->energycost)
29442         {
29443             if(check_energy(COST_CHECK_MP, ANI_SPECIAL))
29444             {
29445                 self->mp -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
29446             }
29447             else
29448             {
29449                 self->health -= self->modeldata.animation[ANI_SPECIAL]->energycost->cost;
29450             }
29451         }
29452     }
29453     self = tmpself;
29454 
29455 }
29456 
29457 
29458 ////////////////////////////////
29459 
anything_walk()29460 void anything_walk()
29461 {
29462     if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80))
29463     {
29464         kill(self);
29465         return;
29466     }
29467     //self->position.x += self->velocity.x;
29468 }
29469 
knife_spawn(char * name,int index,float x,float z,float a,int direction,int type,int map)29470 entity *knife_spawn(char *name, int index, float x, float z, float a, int direction, int type, int map)
29471 {
29472     entity *e = NULL;
29473 
29474     if(index >= 0 || name)
29475     {
29476         e = spawn(x, z, a, direction, name, index, NULL);
29477         if(!e)
29478         {
29479             return NULL;
29480         }
29481         e->ptype = 0;
29482         e->position.y = a;
29483     }
29484     else if(self->weapent && self->weapent->modeldata.project >= 0)
29485     {
29486         e = spawn(x, z, a, direction, NULL, self->weapent->modeldata.project, NULL);
29487         if(!e)
29488         {
29489             return NULL;
29490         }
29491         e->ptype = 0;
29492         e->position.y = a;
29493     }
29494     else if(self->animation->projectile.knife >= 0)
29495     {
29496         e = spawn(x, z, a, direction, NULL, self->animation->projectile.knife, NULL);
29497         if(!e)
29498         {
29499             return NULL;
29500         }
29501         e->ptype = 0;
29502         e->position.y = a;
29503     }
29504     else if(self->animation->projectile.flash >= 0)
29505     {
29506         e = spawn(x, z, 0, direction, NULL, self->animation->projectile.flash, NULL);
29507         if(!e)
29508         {
29509             return NULL;
29510         }
29511         e->ptype = 1;
29512         e->position.y = 0;
29513     }
29514     else if(self->modeldata.knife >= 0)
29515     {
29516         e = spawn(x, z, a, direction, NULL, self->modeldata.knife, NULL);
29517         if(!e)
29518         {
29519             return NULL;
29520         }
29521         e->ptype = 0;
29522         e->position.y = a;
29523     }
29524     else if(self->modeldata.pshotno >= 0)
29525     {
29526         e = spawn(x, z, 0, direction, NULL, self->modeldata.pshotno, NULL);
29527         if(!e)
29528         {
29529             return NULL;
29530         }
29531         e->ptype = 1;
29532         e->position.y = 0;
29533     }
29534     else if(type)
29535     {
29536         e = spawn(x, z, a, direction, "Shot", -1, NULL);
29537         if(!e)
29538         {
29539             return NULL;
29540         }
29541         e->ptype = 0;
29542         e->position.y = a;
29543     }
29544     else
29545     {
29546         e = spawn(x, z, a, direction, "Knife", -1, NULL);
29547         if(!e)
29548         {
29549             return NULL;
29550         }
29551         e->ptype = 0;
29552         e->position.y = a;
29553     }
29554 
29555     if(e == NULL)
29556     {
29557         return NULL;
29558     }
29559     else if(self->modeldata.type & TYPE_PLAYER)
29560     {
29561         e->modeldata.type = TYPE_SHOT;
29562     }
29563     else
29564     {
29565         e->modeldata.type = self->modeldata.type;
29566     }
29567 
29568     if(self->animation->energycost)
29569     {
29570         if(self->animation->energycost->cost > 0 && nocost)
29571         {
29572             self->cantfire = 1;    // Can't fire if still exists on screen
29573         }
29574     }
29575 
29576     if(!e->model->speed && !e->modeldata.nomove)
29577     {
29578         e->modeldata.speed = 2;
29579     }
29580     else if(e->modeldata.nomove)
29581     {
29582         e->modeldata.speed = 0;
29583     }
29584 
29585     e->owner = self;                                                     // Added so projectiles don't hit the owner
29586     e->nograb = 1;                                                       // Prevents trying to grab a projectile
29587     e->attacking = 1;
29588     //e->direction = direction;
29589     e->think = common_think;
29590     e->nextthink = time + 1;
29591     e->trymove = NULL;
29592     e->takedamage = arrow_takedamage;
29593     e->takeaction = NULL;
29594     e->modeldata.aimove = AIMOVE1_ARROW;
29595     if(!e->modeldata.offscreenkill)
29596     {
29597         e->modeldata.offscreenkill = 200;    //default value
29598     }
29599     e->modeldata.aiattack = AIATTACK1_NOATTACK;
29600     e->remove_on_attack = e->modeldata.remove;
29601     e->autokill = e->modeldata.nomove;
29602     e->speedmul = 2;
29603 
29604     ent_set_colourmap(e, map);
29605 
29606     if(e->ptype)
29607     {
29608         e->base = 0;
29609     }
29610     else
29611     {
29612         e->base = a;
29613     }
29614 
29615     if(e->modeldata.hostile < 0)
29616     {
29617         e->modeldata.hostile = self->modeldata.hostile;
29618     }
29619     if(e->modeldata.candamage < 0)
29620     {
29621         e->modeldata.candamage = self->modeldata.candamage;
29622     }
29623     if((self->modeldata.type & TYPE_PLAYER) && ((level && level->nohit) || savedata.mode))
29624     {
29625         e->modeldata.hostile &= ~TYPE_PLAYER;
29626         e->modeldata.candamage &= ~TYPE_PLAYER;
29627     }
29628 
29629     e->modeldata.subject_to_wall = e->modeldata.subject_to_platform = e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
29630     e->modeldata.no_adjust_base  = 1;
29631     return e;
29632 }
29633 
29634 
29635 
bomb_explode()29636 void bomb_explode()
29637 {
29638     if(self->animating)
29639     {
29640         return;
29641     }
29642     kill(self);
29643 }
29644 
29645 
bomb_spawn(char * name,int index,float x,float z,float a,int direction,int map)29646 entity *bomb_spawn(char *name, int index, float x, float z, float a, int direction, int map)
29647 {
29648     entity *e = NULL;
29649 
29650     if(index >= 0 || name)
29651     {
29652         e = spawn(x, z, a, direction, name, index, NULL);
29653     }
29654     else if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && self->weapent->modeldata.project >= 0)
29655     {
29656         e = spawn(x, z, a, direction, NULL, self->weapent->modeldata.project, NULL);
29657     }
29658     else if(self->animation->projectile.bomb >= 0)
29659     {
29660         e = spawn(x, z, a, direction, NULL, self->animation->projectile.bomb, NULL);
29661     }
29662     else if(self->modeldata.bomb >= 0)
29663     {
29664         e = spawn(x, z, a, direction, NULL, self->modeldata.bomb, NULL);
29665     }
29666 
29667     if(e == NULL)
29668     {
29669         return NULL;
29670     }
29671 
29672     e->position.y = a + 0.5;
29673 
29674     if(self->animation->energycost)
29675     {
29676         if(self->animation->energycost->cost > 0 && nocost)
29677         {
29678             self->cantfire = 1;    // Can't fire if still exists on screen
29679         }
29680     }
29681 
29682     if(!e->model->speed && !e->modeldata.nomove)
29683     {
29684         e->modeldata.speed = 2;
29685     }
29686     else if(e->modeldata.nomove)
29687     {
29688         e->modeldata.speed = 0;
29689     }
29690 
29691     e->attacking = 1;
29692     e->owner = self;                                                     // Added so projectiles don't hit the owner
29693     e->nograb = 1;                                                       // Prevents trying to grab a projectile
29694     e->toexplode = 1;                                                    // Set to distinguish exploding projectiles and also so stops falling when hitting an opponent
29695     ent_set_colourmap(e, map);
29696     //e->direction = direction;
29697     toss(e, e->modeldata.jumpheight);
29698     e->think = common_think;
29699     e->nextthink = time + 1;
29700     e->trymove = NULL;
29701     e->takeaction = NULL;
29702     e->modeldata.aimove = AIMOVE1_BOMB;
29703     e->modeldata.aiattack = AIATTACK1_NOATTACK;                                    // Well, bomb's attack animation is passive, dont use any A.I. code.
29704     e->takedamage = common_takedamage;
29705     e->remove_on_attack = 0;
29706     e->autokill = e->modeldata.nomove;
29707     e->speedmul = 2;
29708 
29709 
29710     // Ok, some old mods use type none, will have troubles.
29711     // so we give them some default hostile types.
29712     if(e->modeldata.hostile < 0)
29713     {
29714         e->modeldata.hostile = self->modeldata.hostile;
29715     }
29716     if(e->modeldata.candamage < 0)
29717     {
29718         e->modeldata.candamage = self->modeldata.candamage;
29719     }
29720     e->modeldata.no_adjust_base = 0;
29721     e->modeldata.subject_to_wall = e->modeldata.subject_to_platform = e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
29722     return e;
29723 }
29724 
29725 // Spawn 3 stars
star_spawn(float x,float z,float a,int direction)29726 int star_spawn(float x, float z, float a, int direction)  // added entity to know which star to load
29727 {
29728     entity *e = NULL;
29729     int i, index = -1;
29730     char *starname = NULL;
29731     float fd = (float)((direction ? 2 : -2));
29732 
29733     //merge enemy/player together, use the same rules
29734     if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && self->weapent->modeldata.project >= 0)
29735     {
29736         index = self->weapent->modeldata.project;
29737     }
29738     else if(self->animation->projectile.star >= 0)
29739     {
29740         index = self->animation->projectile.star;    //use any star
29741     }
29742     else if(self->modeldata.star >= 0)
29743     {
29744         index = self->modeldata.star;
29745     }
29746     else
29747     {
29748         starname = "Star";    // this is default star
29749     }
29750 
29751     for(i = 0; i < 3; i++)
29752     {
29753         e = spawn(x, z, a, direction, starname, index, NULL);
29754         if(e == NULL)
29755         {
29756             return 0;
29757         }
29758 
29759         self->attacking = 0;
29760 
29761         e->takedamage = arrow_takedamage;//enemy_takedamage;    // Players can now hit projectiles
29762         e->owner = self;    // Added so enemy projectiles don't hit the owner
29763         e->attacking = 1;
29764         e->nograb = 1;    // Prevents trying to grab a projectile
29765         e->velocity.x = fd * (float)i / 2;
29766         e->think = common_think;
29767         e->nextthink = time + 1;
29768         e->trymove = NULL;
29769         e->takeaction = NULL;
29770         e->modeldata.aimove = AIMOVE1_STAR;
29771         e->modeldata.aiattack = AIATTACK1_NOATTACK;
29772         e->remove_on_attack = e->modeldata.remove;
29773         e->position.y = e->base = a;
29774         e->speedmul = 2;
29775         //e->direction = direction;
29776 
29777         if(e->modeldata.hostile < 0)
29778         {
29779             e->modeldata.hostile = self->modeldata.hostile;
29780         }
29781         if(e->modeldata.candamage < 0)
29782         {
29783             e->modeldata.candamage = self->modeldata.candamage;
29784         }
29785 
29786         e->modeldata.subject_to_wall = e->modeldata.subject_to_platform =
29787                                            e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
29788         e->modeldata.no_adjust_base = 1;
29789     }
29790     return 1;
29791 }
29792 
29793 
29794 
steam_think()29795 void steam_think()
29796 {
29797     if(!self->animating)
29798     {
29799         kill(self);
29800         return;
29801     }
29802 
29803     self->base += 1;
29804     self->position.y = self->base;
29805 }
29806 
29807 
29808 
29809 // for the "trap" type   7-1-2005  trap start
trap_think()29810 void trap_think()
29811 {
29812     self->attacking = 1;
29813 }
29814 //    7-1-2005  trap end
29815 
29816 
29817 
29818 
steam_spawn(float x,float z,float a)29819 void steam_spawn(float x, float z, float a)
29820 {
29821     entity *e = NULL;
29822 
29823     e = spawn(x, z, a, 0, "Steam", -1, NULL);
29824 
29825     if(e == NULL)
29826     {
29827         return;
29828     }
29829 
29830     e->base = a;
29831     e->modeldata.no_adjust_base = 1;
29832     e->think = steam_think;
29833 }
29834 
29835 
29836 
steamer_think()29837 void steamer_think()
29838 {
29839     steam_spawn(self->position.x, self->position.z, self->position.y);
29840     self->nextthink = time + (GAME_SPEED / 10) + (rand32() & 31);
29841 }
29842 
29843 
29844 
text_think()29845 void text_think()     // New function so text can be displayed
29846 {
29847     // wait to suicide
29848     if(!self->animating)
29849     {
29850         kill(self);
29851     }
29852 }
29853 
29854 ////////////////////////////////
29855 
29856 //homing arrow find its target
29857 // type : target type
homing_find_target(int type)29858 entity *homing_find_target(int type)
29859 {
29860     int i, min, max;
29861     int index = -1;
29862     //use the walk animation's range
29863     if(validanim(self, ANI_WALK))
29864     {
29865         min = self->modeldata.animation[ANI_WALK]->range.min.x;
29866         max = self->modeldata.animation[ANI_WALK]->range.max.x;
29867     }
29868     else
29869     {
29870         min = 0;
29871         max = 999;
29872     }
29873     //find the 'nearest' one
29874     for(i = 0; i < ent_max; i++)
29875     {
29876         if( ent_list[i]->exists && ent_list[i] != self //cant target self
29877                 && (ent_list[i]->modeldata.type & type)
29878                 && diff(ent_list[i]->position.x, self->position.x) + diff(ent_list[i]->position.z, self->position.z) >= min
29879                 && diff(ent_list[i]->position.x, self->position.x) + diff(ent_list[i]->position.z, self->position.z) <= max
29880                 && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]  )
29881         {
29882             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))
29883             {
29884                 index = i;
29885             }
29886         }
29887     }
29888     if( index >= 0)
29889     {
29890         return ent_list[index];
29891     }
29892     return NULL;
29893 }
29894 
29895 
bike_crash()29896 void bike_crash()
29897 {
29898     int i;
29899     if(self->direction == DIRECTION_RIGHT)
29900     {
29901         self->velocity.x = 2;
29902     }
29903     else
29904     {
29905         self->velocity.x = -2;
29906     }
29907     for(i = 0; i < levelsets[current_set].maxplayers; i++)
29908     {
29909         control_rumble(i, 100);
29910     }
29911     //if(self->position.x < advancex-100 || self->position.x > advancex+(videomodes.hRes+100)) kill(self);
29912 }
29913 
29914 
29915 
biker_takedamage(entity * other,s_collision_attack * attack)29916 int biker_takedamage(entity *other, s_collision_attack *attack)
29917 {
29918     entity *driver = NULL;
29919     entity *tempself = NULL;
29920     if(self->dead)
29921     {
29922         return 0;
29923     }
29924     // Fell in a hole
29925     if(self->position.y < PIT_DEPTH)
29926     {
29927         kill(self);
29928         return 0;
29929     }
29930     if(other != self)
29931     {
29932         set_opponent(other, self);
29933     }
29934 
29935     if(attack->no_pain) // don't drop driver until it is dead, because the attack has no pain effect
29936     {
29937         checkdamage(other, attack);
29938         if(self->health > 0)
29939         {
29940             return 1;    // not dead yet
29941         }
29942     }
29943 
29944     check_backpain(other,self);
29945     set_pain(self,  self->damagetype, 1);
29946     self->attacking = 1;
29947     if(!self->modeldata.offscreenkill)
29948     {
29949         self->modeldata.offscreenkill = 100;
29950     }
29951     self->think = bike_crash;
29952     // well, this is the real entity, the driver who take the damage
29953     if((driver = drop_driver(self)))
29954     {
29955         driver->position.y = self->position.y;
29956         tempself = self;
29957         self = driver;
29958         self->drop = 1;
29959         self->direction = tempself->direction;
29960         if(self->takedamage)
29961         {
29962             self->takedamage(other, attack);
29963         }
29964         else
29965         {
29966             self->health -= attack->attack_force;
29967         }
29968         self = tempself;
29969 
29970     }
29971     self->health = 0;
29972     checkdeath();
29973     return 1;
29974 }
29975 
29976 
29977 
obstacle_fall()29978 void obstacle_fall()
29979 {
29980     if(inair(self))
29981     {
29982         return;
29983     }
29984 
29985     self->velocity.x = self->velocity.z = 0;
29986     if((!self->animating && validanim(self, ANI_DIE)) || !validanim(self, ANI_DIE))
29987     {
29988         kill(self);    // Fixed so ANI_DIE can be used
29989     }
29990 }
29991 
29992 
29993 
obstacle_fly()29994 void obstacle_fly()    // Now obstacles can fly when hit like on Simpsons/TMNT
29995 {
29996     //self->position.x += self->velocity.x * 4;    // Equivelant of speed 40
29997     if(self->position.x > advancex + (videomodes.hRes + 200) || self->position.x < advancex - 200)
29998     {
29999         kill(self);
30000     }
30001 }
30002 
30003 
30004 
obstacle_takedamage(entity * other,s_collision_attack * attack)30005 int obstacle_takedamage(entity *other, s_collision_attack *attack)
30006 {
30007     if(self->position.y <= PIT_DEPTH)
30008     {
30009         kill(self);
30010         return 0;
30011     }
30012 
30013     //self->pain_time = time + (attack->pain_time?attack->pain_time:(GAME_SPEED / 5));
30014     set_opponent(other, self);
30015     if(self->opponent && (self->opponent->modeldata.type & TYPE_PLAYER))
30016     {
30017         control_rumble(self->opponent->playerindex, 75);
30018     }
30019     checkdamage(other, attack);
30020     self->playerindex = other->playerindex;    // Added so points go to the correct player
30021     addscore(other->playerindex, attack->attack_force * self->modeldata.multiple);  // Points can now be given for hitting an obstacle
30022 
30023     if(self->health <= 0)
30024     {
30025 
30026         checkdeath();
30027 
30028         if(other->position.x < self->position.x)
30029         {
30030             self->velocity.x = 1;
30031         }
30032         else
30033         {
30034             self->velocity.x = -1;
30035         }
30036 
30037         self->attacking = 1;    // So obstacles can explode and hurt players/enemies
30038 
30039         if(self->modeldata.subtype == SUBTYPE_FLYDIE)     // Now obstacles can fly like on Simpsons/TMNT
30040         {
30041             self->velocity.x *= 4;
30042             self->think = obstacle_fly;
30043             ent_set_anim(self, ANI_FALL, 0);
30044         }
30045         else
30046         {
30047             self->think = obstacle_fall;
30048 
30049             if(validanim(self, ANI_DIE))
30050             {
30051                 ent_set_anim(self, ANI_DIE, 0);    //  LTB 1-13-05  Die before toss
30052             }
30053             else
30054             {
30055                 toss(self, self->modeldata.jumpheight / 1.333);
30056                 ent_set_anim(self, ANI_FALL, 0);
30057             }
30058 
30059             if(!self->modeldata.nodieblink)
30060             {
30061                 self->blink = 1;
30062             }
30063         }
30064     }
30065 
30066     self->nextthink = time + 1;
30067     return 1;
30068 }
30069 
30070 
smartspawn(s_spawn_entry * props)30071 entity *smartspawn(s_spawn_entry *props)      // 7-1-2005 Entire section replaced with lord balls code
30072 {
30073     entity *e = NULL;
30074     entity *wp = NULL;
30075     int playercount;
30076 
30077     if( props == NULL /*||
30078         (level == NULL &&
30079          (!selectScreen && !titleScreen && !hallOfFame && !gameOver && !showComplete && !currentScene && !enginecreditsScreen && !menuScreen && !startgameMenu && !newgameMenu && !loadgameMenu &&
30080           !optionsMenu && !controloptionsMenu && !soundoptionsMenu && !videooptionsMenu && !systemoptionsMenu)
30081         )*/
30082       )
30083     {
30084         return NULL;
30085     }
30086 
30087     // Now you can make it so enemies/obstacles/etc only spawn if there are 2 players
30088     if(props->spawnplayer_count >= (playercount = MAX(1, count_ents(TYPE_PLAYER))))
30089     {
30090         if(props->boss && level != NULL)
30091         {
30092             --level->bosses;
30093         }
30094         return NULL;
30095     }
30096 
30097     if( level != NULL && ((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD)) )
30098     {
30099         e = spawn(props->position.x, props->position.z + advancey, props->position.y, props->flip, props->name, props->index, props->model);
30100     }
30101     else
30102     {
30103         e = spawn(props->position.x + advancex, props->position.z, props->position.y, props->flip, props->name, props->index, props->model);
30104     }
30105 
30106     if(e == NULL)
30107     {
30108         return NULL;
30109     }
30110 
30111     //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);
30112 
30113     // Alias?
30114     if(props->alias[0])
30115     {
30116         strncpy(e->name, props->alias, MAX_NAME_LEN);
30117     }
30118     if(props->item)
30119     {
30120         e->item = props->itemindex;
30121     }
30122     if(props->itemalias[0])
30123     {
30124         strncpy(e->itemalias, props->itemalias, MAX_NAME_LEN);
30125     }
30126     if(props->itemmap)
30127     {
30128         e->itemmap = props->itemmap;
30129     }
30130     if(props->itemhealth)
30131     {
30132         e->itemhealth = props->itemhealth;
30133     }
30134     e->itemplayer_count = props->itemplayer_count;
30135 
30136     if(props->spawntype)
30137     {
30138         e->spawntype = props->spawntype;    //2011_03_23, Pass spawntype.
30139     }
30140 
30141     if(props->health[playercount - 1] != 0)
30142     {
30143         e->health = e->modeldata.health = props->health[playercount - 1];
30144     }
30145 
30146     if(props->mp != 0)
30147     {
30148         e->mp = e->modeldata.mp = props->mp;
30149     }
30150 
30151     if(props->score != 0)
30152     {
30153         e->modeldata.score = props->score;    // Overwrite score if exists in the level's. txt file
30154     }
30155     if(props->multiple != 0)
30156     {
30157         e->modeldata.multiple = props->multiple;    // Overwrite multiple if exists in the level's .txt file
30158     }
30159 
30160     if(!e->map && props->colourmap)
30161     {
30162         ent_set_colourmap(e, props->colourmap);
30163     }
30164 
30165     if(props->aggression)
30166     {
30167         e->modeldata.aggression = props->aggression;    // Aggression can be changed with spawn points now
30168     }
30169     if(props->itemtrans)
30170     {
30171         e->itemtrans = props->itemtrans;
30172     }
30173     if(props->alpha)
30174     {
30175         e->modeldata.alpha = props->alpha;
30176     }
30177 
30178     // Feb 26, 2005 - Store the original map to be able to restore with dying flash
30179     if(props->dying)
30180     {
30181         e->dying = props->dying;    // Feb 26, 2005 - Used to define which colourmap is used for the dying flash
30182         e->per1 = props->per1;    // Mar 21, 2005 - Used to store custom percentages
30183         e->per2 = props->per2;    // Mar 21, 2005 - Used to store custom percentages
30184         e->dying2 = props->dying2;    // Dec 15, 2016 - Used to define which colourmap is used for the dying flash for per2 By White Dragon
30185     }
30186 
30187     if(props->nolife)
30188     {
30189         e->modeldata.nolife = props->nolife;    // Overwrite whether live is visible or not
30190     }
30191     e->boss = props->boss;
30192 
30193     if(props->boss && level != NULL && level->bossmusic[0])
30194     {
30195         music(level->bossmusic, 1, level->bossmusic_offset);
30196     }
30197 
30198     // give the entity a weapon item
30199     if(props->weapon)
30200     {
30201         wp = spawn(e->position.x, 100000, 0, 0, props->weapon, props->weaponindex, props->weaponmodel);
30202         if(wp)
30203         {
30204             //ent_default_init(wp);
30205             set_weapon(e, wp->modeldata.weapnum, 0);
30206             e->weapent = wp;
30207         }
30208     }
30209 
30210     // set entity type: player, enemy, npc...
30211     if(props->entitytype)
30212     {
30213         e->modeldata.type = props->entitytype;
30214     }
30215 
30216     // set a parent
30217     if(props->parent) //->varlist->vars->vt != VT_EMPTY
30218     {
30219         e->parent = (entity *)props->parent;
30220     }
30221 
30222     //ent_default_init(e);
30223     execute_onspawn_script(e);
30224     execute_spawn_script(props, e);
30225     return e;
30226 }   // 7-1-2005 replaced section ends here
30227 
is_incam(float x,float z,float a,float threshold)30228 int is_incam(float x, float z, float a, float threshold)
30229 {
30230     if (level)
30231     {
30232         if ( x >= advancex+threshold && x <= advancex+videomodes.hRes-threshold && z-a >= advancey+threshold && z-a <= advancey+videomodes.vRes-4 ) {
30233             if ( x >= scrollminx && x <= scrollmaxx+videomodes.hRes && z >= PLAYER_MIN_Z && z <= PLAYER_MAX_Z ) {
30234                 return 1;
30235             }
30236         }
30237     }
30238 
30239     return 0;
30240 }
30241 
spawnplayer(int index)30242 void spawnplayer(int index)
30243 {
30244     s_spawn_entry p;
30245     //s_model * model = NULL;
30246     int wall;
30247     int xc, zc, find = 0;
30248     index &= 3;
30249 
30250 //    model = find_model(player[index].name);
30251 //    if(model == NULL) return;
30252 
30253     memset(&p, 0, sizeof(p));
30254     p.name = player[index].name;
30255     p.index = -1;
30256     p.itemindex = -1;
30257     p.weaponindex = -1;
30258     p.colourmap = player[index].colourmap;
30259     p.spawnplayer_count = -1;
30260 
30261     if(level->scrolldir & SCROLL_LEFT)
30262     {
30263         if(level->spawn[index].x)
30264         {
30265             p.position.x = (float)(videomodes.hRes - level->spawn[index].x);
30266         }
30267         else
30268         {
30269             p.position.x = (float)((videomodes.hRes - 20) - 30 * index);
30270         }
30271     }
30272     else
30273     {
30274         if(level->spawn[index].x)
30275         {
30276             p.position.x = (float)(level->spawn[index].x);
30277         }
30278         else
30279         {
30280             p.position.x = (float)(20 + 30 * index);
30281         }
30282         p.flip = 1;
30283     }
30284     if(level->spawn[index].z)
30285     {
30286         if(level->scrolldir & (SCROLL_INWARD | SCROLL_OUTWARD))
30287         {
30288             p.position.z = (float)(level->spawn[index].z);
30289         }
30290         else
30291         {
30292             p.position.z = (float)(PLAYER_MIN_Z + level->spawn[index].z);
30293         }
30294     }
30295     else if(PLAYER_MAX_Z - PLAYER_MIN_Z > 5)
30296     {
30297         p.position.z = (float)(PLAYER_MIN_Z + 5);
30298     }
30299     else
30300     {
30301         p.position.z = (float)PLAYER_MIN_Z;
30302     }
30303     if(p.position.z < PLAYER_MIN_Z)
30304     {
30305         p.position.z = PLAYER_MIN_Z;
30306     }
30307     else if(p.position.z > PLAYER_MAX_Z)
30308     {
30309         p.position.z = PLAYER_MAX_Z;
30310     }
30311     //////////////////checking holes/ walls///////////////////////////////////
30312     for(xc = 0; xc < videomodes.hRes / 4; xc++)
30313     {
30314         if(p.position.x > videomodes.hRes)
30315         {
30316             p.position.x -= videomodes.hRes;
30317         }
30318         if(p.position.x < 0)
30319         {
30320             p.position.x += videomodes.hRes;
30321         }
30322         if(PLAYER_MIN_Z == PLAYER_MAX_Z)
30323         {
30324             wall = checkwall(advancex + p.position.x, p.position.z);
30325             if(wall >= 0 && level->walls[wall].height < MAX_WALL_HEIGHT)
30326             {
30327                 break;    //found
30328             }
30329             if(checkhole_in(advancex + p.position.x, p.position.z, p.position.y) || (wall >= 0 && level->walls[wall].height >= MAX_WALL_HEIGHT))
30330             {
30331                 find = 0;
30332             }
30333             else
30334             {
30335                 break;    // found
30336             }
30337         }
30338         else for(zc = 0; zc < (PLAYER_MAX_Z - PLAYER_MIN_Z) / 3; zc++, p.position.z += 3)
30339             {
30340                 if(p.position.z > PLAYER_MAX_Z)
30341                 {
30342                     p.position.z -= PLAYER_MAX_Z - PLAYER_MIN_Z;
30343                 }
30344                 if(p.position.z < PLAYER_MIN_Z)
30345                 {
30346                     p.position.z += PLAYER_MAX_Z - PLAYER_MIN_Z;
30347                 }
30348                 wall = checkwall(advancex + p.position.x, p.position.z);
30349                 if(wall >= 0 && level->walls[wall].height < MAX_WALL_HEIGHT)
30350                 {
30351                     find = 1;
30352                     break;
30353                 }
30354                 else if(wall >= 0 && level->walls[wall].height >= MAX_WALL_HEIGHT)
30355                 {
30356                     continue;
30357                 }
30358                 if(checkhole_in(advancex + p.position.x, p.position.z, p.position.y))
30359                 {
30360                     continue;
30361                 }
30362                 find = 1;
30363                 break;
30364             }
30365         if(find)
30366         {
30367             break;
30368         }
30369         p.position.x += (level->scrolldir & SCROLL_LEFT) ? -4 : 4;
30370     }
30371     ///////////////////////////////////////////////////////////////////////
30372     currentspawnplayer = index;
30373     player[index].ent = smartspawn(&p);
30374 
30375     if(player[index].ent == NULL)
30376     {
30377         shutdown(1, "Fatal: unable to spawn player from '%s'\n", p.name);
30378     }
30379 
30380     player[index].ent->playerindex = index;
30381     if(nomaxrushreset[4] >= 1)
30382     {
30383         player[index].ent->rush.count.max = nomaxrushreset[index];
30384     }
30385     else
30386     {
30387         player[index].ent->rush.count.max = 0;
30388     }
30389 
30390     memset(player[index].combokey, 0, sizeof(*player[index].combokey)*MAX_SPECIAL_INPUTS);
30391     memset(player[index].inputtime, 0, sizeof(*player[index].inputtime)*MAX_SPECIAL_INPUTS);
30392     player[index].combostep = 0;
30393 
30394     if(player[index].spawnhealth)
30395     {
30396         player[index].ent->health = player[index].spawnhealth + 5;
30397     }
30398     if(player[index].ent->health > player[index].ent->modeldata.health)
30399     {
30400         player[index].ent->health = player[index].ent->modeldata.health;
30401     }
30402 
30403     //mp little recorver after a level by tails
30404     if(player[index].spawnmp)
30405     {
30406         player[index].ent->mp = player[index].spawnmp + 2;
30407     }
30408     if(player[index].ent->mp > player[index].ent->modeldata.mp)
30409     {
30410         player[index].ent->mp = player[index].ent->modeldata.mp;
30411     }
30412 
30413     if(player[index].weapnum)
30414     {
30415         set_weapon(player[index].ent, player[index].weapnum, 0);
30416     }
30417     else
30418     {
30419         set_weapon(player[index].ent, level->setweap, 0);
30420     }
30421 }
30422 
time_over()30423 void time_over()
30424 {
30425     int i;
30426     s_collision_attack attack;
30427 
30428     attack = emptyattack;
30429     attack.attack_type = ATK_TIMEOVER;
30430     attack.dropv.y = default_model_dropv.y;
30431     attack.dropv.x = default_model_dropv.x;
30432     attack.dropv.z = default_model_dropv.z;
30433     if(level->type == 1)
30434     {
30435         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
30436     }
30437     else if(!level_completed)
30438     {
30439         endgame = 1;
30440         for(i = 0; i < 4; i++)
30441         {
30442             if(player[i].ent)
30443             {
30444                 endgame = 0;
30445                 self = player[i].ent;
30446                 attack.attack_force = self->health;
30447                 self->takedamage(self, &attack);
30448             }
30449         }
30450 
30451         if(SAMPLE_TIMEOVER >= 0)
30452         {
30453             sound_play_sample(SAMPLE_TIMEOVER, 0, savedata.effectvol, savedata.effectvol, 100);
30454         }
30455 
30456         timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
30457         if(!endgame)
30458         {
30459             showtimeover = 1;
30460         }
30461     }
30462 }
30463 
30464 
30465 // ----------------------- Update functions ------------------------------
30466 
update_scroller()30467 void update_scroller()
30468 {
30469     float to = 0;
30470     int i, againstend = 0;
30471     int numplay = 0; //4player
30472     float tx = advancex, ty = advancey;
30473     float rm = -9999, lm = 999999, bm = -9999, tm = 999999; //player boundary box
30474     static int scrolladd = 0;
30475     scrolldx = scrolldy = 0;
30476 
30477     if(time < level->advancetime || freezeall)
30478     {
30479         return;    // Added freezeall so backgrounds/scrolling don't update if animations are frozen
30480     }
30481 
30482     /*
30483     	//level->advancetime = time + (GAME_SPEED/100);    // Changed so scrolling speeds up for faster players
30484     	level->advancetime = time  -
30485     		((player[0].ent && (player[0].ent->modeldata.speed >= 12 || player[0].ent->modeldata.runspeed >= 12)) ||
30486     		 (player[1].ent && (player[1].ent->modeldata.speed >= 12 || player[1].ent->modeldata.runspeed >= 12)) ||
30487     		 (player[2].ent && (player[2].ent->modeldata.speed >= 12 || player[2].ent->modeldata.runspeed >= 12)) ||
30488     		 (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*/
30489 
30490     level->advancetime = time;
30491 
30492     if(level_completed)
30493     {
30494         return;
30495     }
30496 
30497     if(current_spawn >= level->numspawns && !findent(TYPE_ENEMY) &&
30498             ((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))
30499       )
30500     {
30501         if(!findent(TYPE_ENDLEVEL) && ((!findent(TYPE_ITEM | TYPE_OBSTACLE) && level->type == 1) || level->type == 0)) // Feb 25, 2005 - Added so obstacles
30502         {
30503             level_completed = 1;                                                // can be used for bonus levels
30504         }
30505     }
30506     else if(count_ents(TYPE_ENEMY) < groupmin)
30507     {
30508         while(count_ents(TYPE_ENEMY) < groupmax &&
30509                 current_spawn < level->numspawns &&
30510                 level->pos >= level->spawnpoints[current_spawn].at
30511              )
30512         {
30513             if(level->spawnpoints[current_spawn].musicfade)
30514             {
30515                 musicfade[0] = (float)level->spawnpoints[current_spawn].musicfade;
30516                 musicfade[1] = (float)savedata.musicvol;
30517             }
30518             else if(level->spawnpoints[current_spawn].music[0])
30519             {
30520                 strncpy(musicname, level->spawnpoints[current_spawn].music, 128);
30521                 musicoffset = level->spawnpoints[current_spawn].musicoffset;
30522                 musicloop = 1;
30523             }
30524             else if(level->spawnpoints[current_spawn].wait)
30525             {
30526                 level->waiting = 1;
30527                 go_time = 0;
30528             }
30529             else if(level->spawnpoints[current_spawn].groupmin || level->spawnpoints[current_spawn].groupmax)
30530             {
30531                 groupmin = level->spawnpoints[current_spawn].groupmin;
30532                 groupmax = level->spawnpoints[current_spawn].groupmax;
30533             }
30534             else if(level->spawnpoints[current_spawn].nojoin != 0)
30535             {
30536                 nojoin = (level->spawnpoints[current_spawn].nojoin == 1);
30537             }
30538             else if(level->spawnpoints[current_spawn].scrollminz & 0x80000000)
30539             {
30540                 scrollminz = (float)(level->spawnpoints[current_spawn].scrollminz & 0x7fffffff);
30541                 scrollmaxz = (float)level->spawnpoints[current_spawn].scrollmaxz;
30542                 if(!time)
30543                 {
30544                     advancey = scrollminz;    // reset y if spawn at very beginning
30545                 }
30546             }
30547             else if(level->spawnpoints[current_spawn].scrollminx & 0x80000000)
30548             {
30549                 scrollminx = (float)(level->spawnpoints[current_spawn].scrollminx & 0x7fffffff);
30550                 scrollmaxx = (float)level->spawnpoints[current_spawn].scrollmaxx;
30551             }
30552             else if(level->spawnpoints[current_spawn].blockade)
30553             {
30554                 // assume level spawn entry will not roll back, so just change it to 0 here
30555                 if(level->spawnpoints[current_spawn].blockade < 0)
30556                 {
30557                     level->spawnpoints[current_spawn].blockade = 0;
30558                 }
30559                 blockade = (float)level->spawnpoints[current_spawn].blockade;
30560             }
30561             else if(level->spawnpoints[current_spawn].palette != 0)
30562             {
30563                 // assume level spawn entry will not roll back, so just change it to 0 here
30564                 if(level->spawnpoints[current_spawn].palette < 0)
30565                 {
30566                     level->spawnpoints[current_spawn].palette = 0;
30567                 }
30568                 change_system_palette(level->spawnpoints[current_spawn].palette);
30569             }
30570             else if(level->spawnpoints[current_spawn].light.y)  // change light direction for gfxshadow
30571             {
30572                 light.x = level->spawnpoints[current_spawn].light.x;
30573                 light.y = level->spawnpoints[current_spawn].light.y;
30574             }
30575             else if(level->spawnpoints[current_spawn].shadowcolor)  // change color for gfxshadow
30576             {
30577                 shadowcolor = level->spawnpoints[current_spawn].shadowcolor;
30578                 if(shadowcolor == -1)
30579                 {
30580                     shadowcolor = 0;
30581                 }
30582                 else if(shadowcolor == -2)
30583                 {
30584                     shadowcolor = -1;
30585                 }
30586             }
30587             else if(level->spawnpoints[current_spawn].shadowalpha)  // change color for gfxshadow
30588             {
30589                 shadowalpha = level->spawnpoints[current_spawn].shadowalpha;
30590                 if(shadowalpha == -1)
30591                 {
30592                     shadowalpha = 0;
30593                 }
30594             }
30595             else if(level->spawnpoints[current_spawn].shadowopacity)  // change color for gfxshadow
30596             {
30597                 shadowopacity = level->spawnpoints[current_spawn].shadowopacity;
30598                 if(shadowopacity == -1)
30599                 {
30600                     shadowopacity = 0;
30601                 }
30602                 if(shadowopacity == -2)
30603                 {
30604                     shadowopacity = -1;
30605                 }
30606             }
30607             else
30608             {
30609                 smartspawn(&level->spawnpoints[current_spawn]);
30610             }
30611             ++current_spawn;
30612         }
30613     }
30614 
30615     for(i = 0; i < levelsets[current_set].maxplayers; i++)
30616     {
30617         if(player[i].ent)
30618         {
30619             if(player[i].ent->position.x > rm)
30620             {
30621                 rm = player[i].ent->position.x;
30622             }
30623             if(player[i].ent->position.x < lm)
30624             {
30625                 lm = player[i].ent->position.x;
30626             }
30627             if(player[i].ent->position.z > bm)
30628             {
30629                 bm = player[i].ent->position.z;
30630             }
30631             if(player[i].ent->position.z < tm)
30632             {
30633                 tm = player[i].ent->position.z;
30634             }
30635             numplay++;
30636         }
30637     }
30638 
30639     if(level->waiting)
30640     {
30641         // Wait for all enemies to be defeated
30642         if(!findent(TYPE_ENEMY))
30643         {
30644             level->waiting = 0;
30645             if(level->noreset <= 1)
30646             {
30647                 timeleft = level->settime * COUNTER_SPEED;    // Feb 24, 2005 - This line moved here to set custom time
30648             }
30649             go_time = time + 3 * GAME_SPEED;
30650         }
30651     }
30652     if(numplay == 0)
30653     {
30654         return;
30655     }
30656 
30657 
30658 
30659     if(!level->waiting)
30660     {
30661         if(level->scrolldir & SCROLL_RIGHT)
30662         {
30663 
30664             againstend = (level->width <= videomodes.hRes);
30665 
30666             if(rm - lm > videomodes.hRes)
30667             {
30668                 to = advancex;
30669             }
30670             else
30671             {
30672                 to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset;
30673             }
30674 
30675             if(to < scrollminx)
30676             {
30677                 to = scrollminx;
30678             }
30679             else if(to > scrollmaxx)
30680             {
30681                 to = scrollmaxx;
30682             }
30683 
30684             if((level->scrolldir & SCROLL_BACK) && to < blockade)
30685             {
30686                 to = blockade;
30687             }
30688 
30689             if(to > advancex)
30690             {
30691                 if(to > advancex + level->scrollspeed)
30692                 {
30693                     to = advancex + level->scrollspeed;
30694                 }
30695                 advancex = to;
30696             }
30697             else if((level->scrolldir & SCROLL_BACK) && to < advancex)
30698             {
30699                 if(to < advancex - level->scrollspeed)
30700                 {
30701                     to = advancex - level->scrollspeed;
30702                 }
30703                 advancex = to;
30704             }
30705 
30706             if(advancex < 0)
30707             {
30708                 advancex = 0;
30709             }
30710             if(advancex >= level->width - videomodes.hRes)
30711             {
30712                 advancex = (float)level->width - videomodes.hRes;
30713                 againstend = 1;
30714             }
30715 
30716             if(againstend)
30717             {
30718                 level->pos++;
30719             }
30720             else
30721             {
30722                 level->pos = (int)advancex;
30723             }
30724 
30725 
30726         }
30727         else if(level->scrolldir & SCROLL_LEFT)
30728         {
30729 
30730             againstend = (level->width <= videomodes.hRes);
30731 
30732             if(rm - lm > videomodes.hRes)
30733             {
30734                 to = advancex;
30735             }
30736             else
30737             {
30738                 to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset;
30739             }
30740 
30741             if(to < scrollminx)
30742             {
30743                 to = scrollminx;
30744             }
30745             else if(to > scrollmaxx)
30746             {
30747                 to = scrollmaxx;
30748             }
30749 
30750             if((level->scrolldir & SCROLL_BACK) && level->width - videomodes.hRes - to < blockade)
30751             {
30752                 to = level->width - videomodes.hRes - blockade;
30753             }
30754 
30755             if(to < advancex)
30756             {
30757                 if(to < advancex - level->scrollspeed)
30758                 {
30759                     to = advancex - level->scrollspeed;
30760                 }
30761                 advancex = to;
30762             }
30763             else if((level->scrolldir & SCROLL_BACK) && to > advancex)
30764             {
30765                 if(to > advancex + level->scrollspeed)
30766                 {
30767                     to = advancex + level->scrollspeed;
30768                 }
30769                 advancex = to;
30770             }
30771 
30772             if(advancex > level->width - videomodes.hRes)
30773             {
30774                 advancex = (float)level->width - videomodes.hRes;
30775             }
30776             if(advancex <= 0)
30777             {
30778                 advancex = 0;
30779                 againstend = 1;
30780             }
30781 
30782             if(againstend)
30783             {
30784                 level->pos++;
30785             }
30786             else
30787             {
30788                 level->pos = (int)((level->width - videomodes.hRes) - advancex);
30789             }
30790         }
30791         else if(level->scrolldir & SCROLL_OUTWARD) // z scroll only
30792         {
30793 
30794             if(bm - tm > videomodes.vRes)
30795             {
30796                 to = advancey;
30797             }
30798             else
30799             {
30800                 to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset;
30801             }
30802 
30803             if(to < scrollminz)
30804             {
30805                 to = scrollminz;
30806             }
30807             else if(to > scrollmaxz)
30808             {
30809                 to = scrollmaxz;
30810             }
30811 
30812             if((level->scrolldir & SCROLL_BACK) && to < blockade)
30813             {
30814                 to = blockade;
30815             }
30816 
30817             if(to > advancey)
30818             {
30819                 if(to > advancey + level->scrollspeed)
30820                 {
30821                     to = advancey + level->scrollspeed;
30822                 }
30823                 advancey = to;
30824             }
30825             else if((level->scrolldir & SCROLL_BACK) && to < advancey)
30826             {
30827                 if(to < advancey - level->scrollspeed)
30828                 {
30829                     to = advancey - level->scrollspeed;
30830                 }
30831                 advancey = to;
30832             }
30833 
30834             if(advancey > panel_height - videomodes.vRes)
30835             {
30836                 advancey = (float)panel_height - videomodes.vRes;
30837                 againstend = 1;
30838             }
30839             if(advancey < 0)
30840             {
30841                 advancey = 0;
30842             }
30843 
30844             if(againstend)
30845             {
30846                 level->pos++;
30847             }
30848             else
30849             {
30850                 level->pos = (int)advancey;
30851             }
30852         }
30853         else if(level->scrolldir & SCROLL_INWARD)
30854         {
30855             if(bm - tm > videomodes.vRes)
30856             {
30857                 to = advancey;
30858             }
30859             else
30860             {
30861                 to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset;
30862             }
30863 
30864             if(to < scrollminz)
30865             {
30866                 to = scrollminz;
30867             }
30868             else if(to > scrollmaxz)
30869             {
30870                 to = scrollmaxz;
30871             }
30872 
30873             if((level->scrolldir & SCROLL_BACK) && panel_height - videomodes.vRes - to < blockade)
30874             {
30875                 to = panel_height - videomodes.vRes - blockade;
30876             }
30877 
30878             if(to < advancey)
30879             {
30880                 if(to < advancey - level->scrollspeed)
30881                 {
30882                     to = advancey - level->scrollspeed;
30883                 }
30884                 advancey = to;
30885             }
30886             else if((level->scrolldir & SCROLL_BACK) && to > advancey)
30887             {
30888                 if(to > advancey + level->scrollspeed)
30889                 {
30890                     to = advancey + level->scrollspeed;
30891                 }
30892                 advancey = to;
30893             }
30894 
30895             if(advancey > panel_height - videomodes.vRes)
30896             {
30897                 advancey = (float)panel_height - videomodes.vRes;
30898             }
30899             if(advancey <= 0)
30900             {
30901                 advancey = 0;
30902                 againstend = 1;
30903             }
30904 
30905             if(againstend)
30906             {
30907                 level->pos++;
30908             }
30909             else
30910             {
30911                 level->pos = (int)((panel_height - videomodes.vRes) - advancey);
30912             }
30913         }
30914         //up down, elevator stage
30915         else if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN))
30916         {
30917             //advancey += 0.5;
30918             if(scrolladd == 1)
30919             {
30920                 scrolladd = 0;
30921                 advancey++;
30922             }
30923             else
30924             {
30925                 scrolladd++;
30926             }
30927             level->pos = (int)advancey;
30928         }
30929     }//if(!level->waiting)
30930 
30931     // z auto-scroll
30932     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
30933     {
30934 
30935         if(cameratype == 1)
30936         {
30937             bm = -9999;
30938             tm = 999999; //recalculate
30939             for(i = 0; i < levelsets[current_set].maxplayers; i++)
30940             {
30941                 if(player[i].ent)
30942                 {
30943                     if(player[i].ent->position.z - player[i].ent->position.y > bm)
30944                     {
30945                         bm = player[i].ent->position.z - player[i].ent->position.y;
30946                     }
30947                     if(player[i].ent->position.z - player[i].ent->position.y < tm)
30948                     {
30949                         tm = player[i].ent->position.z - player[i].ent->position.y;
30950                     }
30951                 }
30952             }
30953         }
30954         if(bm - tm > videomodes.vRes)
30955         {
30956             to = advancey;
30957         }
30958         else
30959         {
30960             to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset;
30961         }
30962 
30963         // new scroll limit
30964         if(to > scrollmaxz)
30965         {
30966             to = scrollmaxz;
30967         }
30968         else if(to < scrollminz)
30969         {
30970             to = scrollminz;
30971         }
30972 
30973         if(to != advancey)
30974         {
30975             if(to > advancey + level->scrollspeed)
30976             {
30977                 to = advancey + level->scrollspeed;
30978             }
30979             else if(to < advancey - level->scrollspeed)
30980             {
30981                 to = advancey - level->scrollspeed;
30982             }
30983             advancey = (float)to;
30984         }
30985 
30986         if(advancey > panel_height - (level->rocking ? 16 : 12) - videomodes.vRes)
30987         {
30988             advancey = (float)(panel_height - (level->rocking ? 16 : 12) - videomodes.vRes);
30989         }
30990         if(advancey < 0)
30991         {
30992             advancey = 0;
30993         }
30994     }
30995     // now x auto scroll
30996     else if((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD))
30997     {
30998         if(rm - lm > videomodes.hRes)
30999         {
31000             to = advancex;
31001         }
31002         else
31003         {
31004             to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset;
31005         }
31006 
31007         // new scroll limit
31008         if(to > scrollmaxx)
31009         {
31010             to = scrollmaxx;
31011         }
31012         else if(to < scrollminx)
31013         {
31014             to = scrollminx;
31015         }
31016 
31017         if(to != advancex)
31018         {
31019             if(to > advancex + level->scrollspeed)
31020             {
31021                 to = advancex + level->scrollspeed;
31022             }
31023             else if(to < advancex - level->scrollspeed)
31024             {
31025                 to = advancex - level->scrollspeed;
31026             }
31027             advancex = (float)to;
31028         }
31029 
31030         if(advancex > level->width - videomodes.hRes)
31031         {
31032             advancex = (float)(level->width - videomodes.hRes);
31033         }
31034         if(advancex < 0)
31035         {
31036             advancex = 0;
31037         }
31038     }
31039     //end of z auto-scroll
31040     // global value for type_panel
31041     scrolldx = advancex - tx;
31042     scrolldy = advancey - ty;
31043 }
31044 
31045 
update_scrolled_bg()31046 void update_scrolled_bg()
31047 {
31048     int i = 0;
31049     float rocktravel;
31050     unsigned char neonp[32];//3*8
31051     static int neon_count = 0;
31052     static int rockpos = 0;
31053     static int rockoffssine[32] =
31054     {
31055         2, 2, 3, 4, 5, 6, 7, 7,
31056         8, 8, 9, 9, 9, 9, 8, 8,
31057         7, 7, 6, 5, 4, 3, 2, 2,
31058         1, 1, 0, 0, 0, 0, 1, 1
31059     };   // normal rock
31060     static int rockoffsshake[32] =
31061     {
31062         2, 2, 2, 2, 2, 2, 2, 2,
31063         2, 2, 0, 4, 2, 0, 4, 2,
31064         2, 2, 2, 2, 2, 2, 2, 2,
31065         2, 2, 0, 4, 2, 0, 4, 2,
31066     };   // slow, constant jarring rock, like on a train
31067     static int rockoffsrumble[32] =
31068     {
31069         2, 2, 3, 3, 2, 2, 3, 3,
31070         2, 2, 3, 3, 2, 3, 2, 3,
31071         2, 2, 3, 3, 2, 2, 3, 3,
31072         2, 2, 3, 3, 2, 3, 2, 3,
31073     };   // fast, constant rumbling, like in/on a van or trailer
31074     int pb = pixelbytes[(int)screenformat];
31075 
31076     if(time >= neon_time && !freezeall)   // Added freezeall so neon lights don't update if animations are frozen
31077     {
31078         if(pixelformat == PIXEL_8) // under 8bit mode just cycle the palette from 128 to 135
31079         {
31080             for(i = 0; i < 8; i++)
31081             {
31082                 neontable[128 + i] = 128 + ((i + neon_count) & 7);
31083             }
31084         }
31085         else if(pixelformat == PIXEL_x8) // copy palette under 16/32bit mode
31086         {
31087             memcpy(neonp, neontable + 128 * pb, 8 * pb);
31088             memcpy(neontable + 128 * pb, neonp + 2 * pb, 6 * pb);
31089             memcpy(neontable + (128 + 6)*pb, neonp, 2 * pb);
31090         }
31091         neon_time = time + (GAME_SPEED / 3);
31092         neon_count += 2;
31093     }
31094 
31095     if(!freezeall)
31096     {
31097         rocktravel = (level->rocking) ? ((time - traveltime) / ((float)GAME_SPEED / 30)) : 0; // no like in real life, maybe
31098         if(level->bgspeed < 0)
31099         {
31100             rocktravel = -rocktravel;
31101         }
31102         bgtravelled  += (time - traveltime) * level->bgspeed  / 30 * 4 + rocktravel;
31103         vbgtravelled += (time - traveltime) * level->vbgspeed / 30 * 4;
31104     }
31105     else
31106     {
31107         texttime += time - traveltime;
31108     }
31109 
31110     timevar = time - texttime;
31111 
31112     if(level->rocking)
31113     {
31114         rockpos = (timevar / (GAME_SPEED / 8)) & 31;
31115         if(level->rocking == 1)
31116         {
31117             gfx_y_offset = level->quake - 4 - rockoffssine[rockpos];
31118         }
31119         else if(level->rocking == 2)
31120         {
31121             gfx_y_offset = level->quake - 4 - rockoffsshake[rockpos];
31122         }
31123         else if(level->rocking == 3)
31124         {
31125             gfx_y_offset = level->quake - 4 - rockoffsrumble[rockpos];
31126         }
31127     }
31128     else
31129     {
31130         if(level->quake >= 0)
31131         {
31132             gfx_y_offset = level->quake - 4;
31133         }
31134         else
31135         {
31136             gfx_y_offset = level->quake + 4;
31137         }
31138     }
31139 
31140     //if(level->scrolldir!=SCROLL_UP && level->scrolldir!=SCROLL_DOWN) gfx_y_offset -= advancey;
31141     gfx_y_offset += gfx_y_offset_adj;   //2011_04_03, DC: Apply modder adjustment.
31142 
31143     traveltime = time;
31144 
31145     if(time >= level->quaketime)
31146     {
31147         level->quake /= 2;
31148         level->quaketime = time + (GAME_SPEED / 25);
31149     }
31150 }
31151 
draw_scrolled_bg()31152 void draw_scrolled_bg()
31153 {
31154     int index = 0, x, z, i = 0, j, l, m;
31155     s_layer *layer;
31156     int width, height;
31157 
31158     int vpx, vpy, vpw, vph;
31159 
31160     if(viewportw > 0)
31161     {
31162         vpx = viewportx;
31163         vpy = viewporty;
31164         vpw = viewportw;
31165         vph = viewporth;
31166     }
31167     else
31168     {
31169         vpx = vpy = 0;
31170         vpw = videomodes.hRes;
31171         vph = videomodes.vRes;
31172     }
31173 
31174     //if(level) printf("%d %d %d %d\n", vpx, vpy, vpw, vph);
31175 
31176     s_drawmethod screenmethod = plainmethod, *pscreenmethod = &screenmethod;
31177 
31178     for(i = 0; i < level->numholes; i++)
31179     {
31180         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);
31181     }
31182 
31183     for(index = 0; index < level->numlayersref; index++)
31184     {
31185         layer = level->layersref + index;
31186 
31187         screenmethod = layer->drawmethod;
31188 
31189         //printf("layer %d, handle:%u, z:%d\n", index, layer->gfx.handle, layer->position.z);
31190 
31191         if(!layer->drawmethod.xrepeat || !layer->drawmethod.yrepeat || !layer->enabled)
31192         {
31193             continue;
31194         }
31195 
31196         width = screenmethod.xspan = layer->size.x + layer->spacing.x;
31197         height = screenmethod.yspan = layer->size.y + layer->spacing.z;
31198 
31199         x = (int)(layer->offset.x - (advancex +  bgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.x) ) ;
31200 
31201         //printf("layerxratio %f  %d %f\n ", layer->xratio, x, layer->bgspeedratio);
31202 
31203         if((level->scrolldir & SCROLL_UP))
31204         {
31205             //z = (int)(layer->offset.z + advancey * (1.0 - layer->ratio.z) ) ;
31206             z = (int)(layer->offset.z - (advancey + vbgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.z) ) ;
31207         }
31208         else
31209         {
31210             //z = (int)(layer->offset.z - advancey * (1.0 - layer->ratio.z) ) ;
31211             z = (int)(layer->offset.z - (advancey + vbgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.z) ) ;
31212         }
31213 
31214         if(layer->quake)
31215         {
31216             x += gfx_x_offset;
31217             z += gfx_y_offset;
31218             //printf("%d y %d %d\n", index, gfx_y_offset, z);
31219         }
31220 
31221         x -= vpx;
31222         z -= vpy;
31223 
31224 
31225         if(x < 0)
31226         {
31227             i = (-x) / width;
31228             x %= width;
31229         }
31230         else
31231         {
31232             i = 0;
31233         }
31234 
31235         if(i > 0 && screenmethod.water.watermode != 3  && screenmethod.water.amplitude)
31236         {
31237             i--;
31238             x -= width;
31239         }
31240 
31241         if(z < 0)
31242         {
31243             j = (-z) / height;
31244             z %= height;
31245         }
31246         else
31247         {
31248             j = 0;
31249         }
31250         if(layer->neon)
31251         {
31252             if(pixelformat != PIXEL_x8 || current_palette <= 0)
31253             {
31254                 screenmethod.table = neontable;
31255             }
31256         }
31257         else
31258         {
31259             screenmethod.table = (pixelformat == PIXEL_x8) ? (current_palette > 0 ? (level->palettes[current_palette - 1]) : NULL) : NULL;
31260         }
31261         screenmethod.water.wavetime =  (int)(timevar * screenmethod.water.wavespeed);
31262         screenmethod.xrepeat = screenmethod.yrepeat = 0;
31263         for(m = z; j < layer->drawmethod.yrepeat && m < vph; m += height, j++, screenmethod.yrepeat++);
31264         for(l = x; i < layer->drawmethod.xrepeat && l < vpw + (screenmethod.water.watermode == 3 ? 0 : screenmethod.water.amplitude * 2); l += width, i++, screenmethod.xrepeat++);
31265 
31266         if(layer->gfx.screen->magic == screen_magic)
31267         {
31268             spriteq_add_screen(x + vpx, z + vpy, layer->z, layer->gfx.screen, &screenmethod, index);
31269         }
31270         else if(layer->gfx.sprite->magic == sprite_magic)
31271         {
31272             spriteq_add_frame(x + vpx, z + vpy, layer->z, layer->gfx.sprite, &screenmethod, index);
31273         }
31274 
31275         //printf("******%d\t%d\t%d\t%d\t%d*****\n", x+vpx, z+vpy, layer->.z, screenmethod.xrepeat, screenmethod.yrepeat);
31276     }
31277 
31278 
31279 }
31280 
getinterval()31281 u32 getinterval()
31282 {
31283     interval = timer_getinterval(GAME_SPEED); // so interval can be logged into movie
31284     if(interval > GAME_SPEED)
31285     {
31286         interval = 1;
31287     }
31288     if(interval > GAME_SPEED / 4)
31289     {
31290         interval = GAME_SPEED / 4;
31291     }
31292     return interval;
31293 }
31294 
inputrefresh(int playrecmode)31295 void inputrefresh(int playrecmode)
31296 {
31297     int p;
31298     s_player *pl;
31299     u32 k;
31300 
31301     control_update(playercontrolpointers, MAX_PLAYERS);
31302 
31303     bothkeys = 0;
31304     bothnewkeys = 0;
31305 
31306     for(p = 0; p < MAX_PLAYERS; p++)
31307     {
31308         pl = player + p;
31309 
31310         if ( playrecmode != A_REC_PLAY )
31311         {
31312 
31313             pl->releasekeys = (playercontrolpointers[p]->keyflags | pl->keys) - playercontrolpointers[p]->keyflags;
31314             pl->releasekeys &= ~pl->disablekeys;
31315             pl->keys = playercontrolpointers[p]->keyflags & ~pl->disablekeys;
31316             pl->newkeys = playercontrolpointers[p]->newkeyflags & ~pl->disablekeys;
31317             pl->playkeys |= pl->newkeys;
31318             pl->playkeys &= pl->keys;
31319             pl->playkeys &= ~pl->disablekeys;
31320         }
31321         else
31322         {
31323             // in play mode: add pressed keys to rec keys
31324             pl->releasekeys |= (playercontrolpointers[p]->keyflags | pl->prevkeys) - playercontrolpointers[p]->keyflags;
31325             pl->releasekeys &= ~pl->disablekeys;
31326             pl->keys |= playercontrolpointers[p]->keyflags & ~pl->disablekeys;
31327             pl->newkeys |= playercontrolpointers[p]->newkeyflags & ~pl->disablekeys;
31328             pl->playkeys |= pl->newkeys;
31329             pl->playkeys &= pl->keys;
31330             pl->playkeys &= ~pl->disablekeys;
31331         }
31332 
31333         if(pl->ent && pl->ent->movetime < time)
31334         {
31335             memset(pl->combokey, 0, sizeof(*pl->combokey)*MAX_SPECIAL_INPUTS);
31336             memset(pl->inputtime, 0, sizeof(*pl->inputtime)*MAX_SPECIAL_INPUTS);
31337             pl->combostep = 0;
31338         }
31339         if(pl->newkeys)
31340         {
31341             k = pl->newkeys;
31342             if(pl->ent)
31343             {
31344                 pl->ent->movetime = time + GAME_SPEED / 4;
31345                 if(k & FLAG_MOVELEFT)
31346                 {
31347                     k |= pl->ent->direction ? FLAG_BACKWARD : FLAG_FORWARD;
31348                 }
31349                 else if(k & FLAG_MOVERIGHT)
31350                 {
31351                     k |= pl->ent->direction ? FLAG_FORWARD : FLAG_BACKWARD;
31352                 }
31353             }
31354             pl->inputtime[pl->combostep] = time;
31355             pl->combokey[pl->combostep] = k;
31356             pl->combostep++;
31357             pl->combostep %= MAX_SPECIAL_INPUTS;
31358         }
31359 
31360         bothkeys |= player[p].keys;
31361         bothnewkeys |= player[p].newkeys;
31362     }
31363 
31364 }
31365 
execute_keyscripts()31366 void execute_keyscripts()
31367 {
31368     int p;
31369     for(p = 0; p < levelsets[current_set].maxplayers; p++)
31370     {
31371         if(!pause && (level || inScreen) && (player[p].newkeys || (keyscriptrate && player[p].keys) || player[p].releasekeys))
31372         {
31373             if(level)
31374             {
31375                 execute_level_key_script(p);
31376                 execute_entity_key_script(player[p].ent);
31377             }
31378             execute_key_script(p);
31379             execute_key_script_all(p);
31380         }
31381     }
31382 }
31383 
execute_updatescripts()31384 void execute_updatescripts()
31385 {
31386     if(Script_IsInitialized(&update_script))
31387     {
31388         Script_Execute(&(update_script));
31389     }
31390     if(level && Script_IsInitialized(&(level->update_script)))
31391     {
31392         Script_Execute(&(level->update_script));
31393     }
31394 }
31395 
execute_updatedscripts()31396 void execute_updatedscripts()
31397 {
31398     if(Script_IsInitialized(&updated_script))
31399     {
31400         Script_Execute(&(updated_script));
31401     }
31402     if(level && Script_IsInitialized(&(level->updated_script)))
31403     {
31404         Script_Execute(&(level->updated_script));
31405     }
31406 }
31407 
draw_textobjs()31408 void draw_textobjs()
31409 {
31410     int i;
31411     s_textobj *textobj;
31412     if(!level)
31413     {
31414         return;
31415     }
31416     for(i = 0; i < level->numtextobjs ; i++)
31417     {
31418         textobj = level->textobjs + i;
31419 
31420         if(textobj->time && textobj->time <= time)		//If a time was set and passed, remove the text object.
31421         {
31422             level->textobjs[i].time	= 0;
31423             level->textobjs[i].position.x = 0;
31424             level->textobjs[i].position.y = 0;
31425             level->textobjs[i].font = 0;
31426             level->textobjs[i].position.z = 0;
31427             if(level->textobjs[i].text)
31428             {
31429                 free(level->textobjs[i].text);
31430                 level->textobjs[i].text = NULL;
31431             }
31432         }
31433         else
31434         {
31435             if(textobj->text)
31436             {
31437                 font_printf(textobj->position.x, textobj->position.y, textobj->font, textobj->position.z, "%s", textobj->text);
31438             }
31439         }
31440     }
31441 }
31442 
recordInputs()31443 int recordInputs()
31444 {
31445     int p = 0;
31446     RecKeys reckey;
31447     unsigned int window = 4096;
31448     u32 max_rec_time = GAME_SPEED*60*10; // protection
31449 
31450     if(playrecstatus->status != A_REC_REC) return 0;
31451     if ( !playrecstatus->begin )
31452     {
31453         playrecstatus->starttime = time;
31454         playrecstatus->synctime = 0;
31455         if (playrecstatus->buffer)
31456         {
31457             free(playrecstatus->buffer);
31458             playrecstatus->buffer = NULL;
31459         }
31460         playrecstatus->buffer = (RecKeys*)calloc(window+playrecstatus->synctime*sizeof(RecKeys),sizeof(RecKeys));
31461         if (playrecstatus->buffer == NULL)
31462         {
31463             printf("Error to allocate buffer in record inputs mode.\n");
31464             return 0;
31465         }
31466         playrecstatus->begin = 1;
31467         playrecstatus->seed = getseed();
31468         playrecstatus->cseed = time;
31469         srand(time);
31470         playrecstatus->ticks = timer_gettick();
31471     }
31472     else
31473     {
31474         if ( playrecstatus->synctime%window >= window-2 ) // last is NULL bytes
31475         {
31476             playrecstatus->buffer = (RecKeys*)realloc(playrecstatus->buffer,sizeof(RecKeys)*((int)(trunc(playrecstatus->synctime/window)+1)*window+window));
31477             if (playrecstatus->buffer == NULL)
31478             {
31479                 printf("Error to allocate buffer in record inputs mode.\n");
31480                 return 0;
31481             } 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
31482         }
31483     }
31484 
31485     // now rec!
31486     if(playrecstatus->buffer && playrecstatus->begin)
31487     {
31488         for ( p = 0; p < MAX_PLAYERS; p++ )
31489         {
31490             reckey.keys[p]        = player[p].keys;
31491             reckey.newkeys[p]     = player[p].newkeys;
31492             reckey.releasekeys[p] = player[p].releasekeys;
31493             reckey.playkeys[p]    = player[p].playkeys;
31494         }
31495         reckey.time     = time;
31496         reckey.interval = interval;
31497         reckey.synctime = playrecstatus->synctime;
31498         //reckey.seed     = getseed();
31499         memcpy( &playrecstatus->buffer[playrecstatus->synctime], &reckey, sizeof(reckey) );
31500     }
31501 
31502     if ( time >= max_rec_time ) stopRecordInputs(); // safe
31503     if(playrecstatus->status == A_REC_REC) ++playrecstatus->synctime;
31504 
31505     //debug_printf("time: %d sync: %d",(u32)time,(u32)playrecstatus->synctime);
31506     //debug_printf("keys: %d",player[0].releasekeys&FLAG_ATTACK);
31507 
31508     return 1;
31509 }
31510 
playRecordedInputs()31511 int playRecordedInputs()
31512 {
31513     int p = 0;
31514     RecKeys reckey;
31515     char path[MAX_ARG_LEN + 1];
31516     size_t filesize = 0;
31517     char header[6];
31518 
31519     if(playrecstatus->status != A_REC_PLAY) return 0;
31520     if ( !playrecstatus->begin ) //time >= playrecstatus->starttime &&
31521     {
31522         if ( strlen(playrecstatus->path) <= 0 ) getBasePath(path, "Saves", 0);
31523         else strcpy(path,playrecstatus->path);
31524         if ( path[strlen(path)-1] != '/' ) strcpy(path,"/");
31525 
31526         if (playrecstatus->buffer)
31527         {
31528             free(playrecstatus->buffer);
31529             playrecstatus->buffer = NULL;
31530         }
31531         if(playrecstatus->handle)
31532         {
31533             fclose(playrecstatus->handle);
31534         }
31535 
31536         playrecstatus->handle = fopen(strcat(path,playrecstatus->filename), "rb+");
31537         if(playrecstatus->handle)
31538         {
31539             fseek(playrecstatus->handle, 0L, SEEK_END);
31540             filesize = ftell(playrecstatus->handle);
31541             fseek(playrecstatus->handle, 0L, SEEK_SET); // or rewind(playrecstatus->handle)
31542 
31543             if (filesize <= 0)
31544             {
31545                 printf("Empty recorded inputs file.\n");
31546                 return 0;
31547             }
31548             playrecstatus->buffer = (RecKeys*)calloc(1,filesize+1);
31549             if(!playrecstatus->buffer)
31550             {
31551                 printf("Error to allocate buffer for recorded inputs.\n");
31552                 return 0;
31553             }
31554         } else
31555         {
31556             printf("Error to open recorded inputs file.\n");
31557             return 0;
31558         }
31559         fread(&header, 6, 1, playrecstatus->handle);
31560         fread(&playrecstatus->starttime, sizeof(u32), 1, playrecstatus->handle);
31561         fread(&playrecstatus->endtime, sizeof(u32), 1, playrecstatus->handle);
31562         fread(&playrecstatus->totsynctime, sizeof(u32), 1, playrecstatus->handle);
31563         fread(&playrecstatus->cseed, sizeof(u32), 1, playrecstatus->handle);
31564         fread(&playrecstatus->seed, sizeof(unsigned long), 1, playrecstatus->handle);
31565         fread(&playrecstatus->ticks, sizeof(unsigned), 1, playrecstatus->handle);
31566         fread(playrecstatus->buffer, sizeof(RecKeys)*(playrecstatus->endtime+1), 1, playrecstatus->handle);
31567 
31568         // sync at start time
31569         time = playrecstatus->starttime;
31570         playrecstatus->synctime = 0;
31571         playrecstatus->begin = 1;
31572         srand(playrecstatus->cseed);
31573         srand32(playrecstatus->seed);
31574         //set_ticks(timer_gettick()-playrecstatus->ticks);
31575     }
31576 
31577     // now play!
31578     if(playrecstatus->buffer && playrecstatus->begin && playrecstatus->synctime < playrecstatus->totsynctime)
31579     {
31580         memcpy( &reckey, &playrecstatus->buffer[playrecstatus->synctime], sizeof(reckey) );
31581 
31582         if( time != reckey.time )
31583         {
31584             u32 nextsynctime = reckey.synctime;
31585 
31586             //time = (u32)reckey.time;
31587             printf("Play recorded inputs: Out of sync! Time: %d, RecTime: %d\n",time,reckey.time);
31588             /*if ( interval != reckey.interval )
31589             {
31590                 //interval = (u32)reckey.interval;
31591                 printf("Play recorded inputs: Out of sync! Interval: %d, RecInterval: %d\n",interval,reckey.interval);
31592             }*/
31593 
31594             while( time > reckey.time && nextsynctime > 0 ) {
31595                 memcpy( &reckey, &playrecstatus->buffer[--nextsynctime], sizeof(reckey) );
31596             }
31597             time = reckey.time;
31598 
31599             while( time < reckey.time && nextsynctime < playrecstatus->totsynctime ) {
31600                 memcpy( &reckey, &playrecstatus->buffer[++nextsynctime], sizeof(reckey) );
31601             }
31602             time = reckey.time;
31603         }
31604 
31605         if ( time == reckey.time )
31606         {
31607             if ( playrecstatus->synctime != reckey.synctime )
31608             {
31609                 u32 nextsynctime = reckey.synctime;
31610 
31611                 printf("Play recorded inputs: Out of sync! SyncTime: %d, RecSyncTime: %d\n",playrecstatus->synctime,reckey.synctime);
31612 
31613                 while( playrecstatus->synctime > reckey.synctime && nextsynctime > 0 ) {
31614                     memcpy( &reckey, &playrecstatus->buffer[--nextsynctime], sizeof(reckey) );
31615                 }
31616                 playrecstatus->synctime = reckey.synctime;
31617 
31618                 while( playrecstatus->synctime < reckey.synctime && nextsynctime < playrecstatus->totsynctime ) {
31619                     memcpy( &reckey, &playrecstatus->buffer[++nextsynctime], sizeof(reckey) );
31620                 }
31621                 playrecstatus->synctime = reckey.synctime;
31622             }
31623 
31624             if ( interval != reckey.interval ) interval = reckey.interval;
31625             //srand32(reckey.seed);
31626             for ( p = 0; p < MAX_PLAYERS; p++ )
31627             {
31628                 player[p].keys        = reckey.keys[p];
31629                 player[p].newkeys     = reckey.newkeys[p];
31630                 player[p].releasekeys = reckey.releasekeys[p];
31631                 player[p].playkeys    = reckey.playkeys[p];
31632             }
31633             //inputrefresh(playrecstatus->status);
31634         }
31635     }
31636 
31637     //debug_printf("synctim: %d totsync: %d status:%d",playrecstatus->synctime,playrecstatus->totsynctime,playrecstatus->status);
31638     if(playrecstatus->status == A_REC_PLAY) ++playrecstatus->synctime;
31639     if ( playrecstatus->synctime >= playrecstatus->totsynctime || time >= playrecstatus->endtime ) stopRecordInputs();
31640 
31641     //debug_printf("time: %d sync: %d",(u32)time,(u32)playrecstatus->synctime);
31642     //debug_printf("keys: %d",player[0].releasekeys&FLAG_ATTACK);
31643 
31644     return 1;
31645 }
31646 
stopRecordInputs()31647 int stopRecordInputs()
31648 {
31649     if(playrecstatus)
31650     {
31651         switch(playrecstatus->status)
31652         {
31653             case A_REC_REC:
31654             {
31655                 char path[MAX_ARG_LEN + 1];
31656                 char header[6] = "INP10";
31657 
31658                 if ( strlen(playrecstatus->path) <= 0 ) getBasePath(path, "Saves", 0);
31659                 else strcpy(path,playrecstatus->path);
31660                 if ( path[strlen(path)-1] != '/' ) strcpy(path,"/");
31661 
31662                 if (playrecstatus->buffer)
31663                 {
31664                     playrecstatus->handle = fopen(strcat(path,playrecstatus->filename), "wb+");
31665                     if(playrecstatus->handle)
31666                     {
31667                         playrecstatus->endtime = time;
31668                         if (playrecstatus->synctime < 2) playrecstatus->synctime = 2;
31669                         else playrecstatus->synctime -= 2;
31670 
31671                         fwrite(header, 6, 1, playrecstatus->handle);
31672                         fwrite(&playrecstatus->starttime, sizeof(u32), 1, playrecstatus->handle);
31673                         fwrite(&playrecstatus->endtime, sizeof(u32), 1, playrecstatus->handle);
31674                         fwrite(&playrecstatus->synctime, sizeof(u32), 1, playrecstatus->handle);
31675                         fwrite(&playrecstatus->cseed, sizeof(u32), 1, playrecstatus->handle);
31676                         fwrite(&playrecstatus->seed, sizeof(unsigned long), 1, playrecstatus->handle);
31677                         fwrite(&playrecstatus->ticks, sizeof(unsigned), 1, playrecstatus->handle);
31678                         fwrite(playrecstatus->buffer, sizeof(RecKeys)*(playrecstatus->synctime+1), 1, playrecstatus->handle);
31679                         fflush(playrecstatus->handle); // safe
31680                         fclose(playrecstatus->handle);
31681                     } else return 0;
31682 
31683                     free(playrecstatus->buffer);
31684                     playrecstatus->buffer = NULL;
31685                 } else return 0;
31686                 break;
31687             }
31688             case A_REC_PLAY:
31689             {
31690                 if(playrecstatus->handle)
31691                 {
31692                     if (playrecstatus->handle) fclose(playrecstatus->handle);
31693                     else return 0;
31694                 }
31695                 break;
31696             }
31697             case A_REC_STOP:
31698             {
31699                 if(playrecstatus->handle)
31700                 {
31701                     if (playrecstatus->handle) fclose(playrecstatus->handle);
31702                     else return 0;
31703                 }
31704                 break;
31705             }
31706         }
31707 
31708         playrecstatus->status = A_REC_STOP;
31709         playrecstatus->begin = 0;
31710         playrecstatus->synctime = 0;
31711         freeRecordedInputs();
31712     } return 0;
31713 
31714     return 1;
31715 }
31716 
freeRecordedInputs()31717 int freeRecordedInputs()
31718 {
31719     playrecstatus->status = A_REC_STOP;
31720     if(playrecstatus->handle) fclose(playrecstatus->handle);
31721     if(playrecstatus->buffer)
31722     {
31723         free(playrecstatus->buffer);
31724         playrecstatus->buffer = NULL;
31725         return 1;
31726     }
31727 
31728     return 0;
31729 }
31730 
init_input_recorder()31731 a_playrecstatus* init_input_recorder()
31732 {
31733     playrecstatus = (a_playrecstatus*)calloc(1,sizeof(*playrecstatus));
31734 
31735     return playrecstatus;
31736 }
31737 
free_input_recorder()31738 void free_input_recorder()
31739 {
31740     if(playrecstatus)
31741     {
31742         if(playrecstatus->buffer)
31743         {
31744             if(playrecstatus->handle) fclose(playrecstatus->handle);
31745             free(playrecstatus->buffer);
31746             playrecstatus->buffer = NULL;
31747         }
31748         free(playrecstatus);
31749         playrecstatus = NULL;
31750     }
31751 }
31752 
update(int ingame,int usevwait)31753 void update(int ingame, int usevwait)
31754 {
31755     getinterval();
31756     if(playrecstatus->status == A_REC_PLAY && !pause && level) if ( !playRecordedInputs() ) stopRecordInputs();
31757     inputrefresh(playrecstatus->status);
31758     if(playrecstatus->status == A_REC_REC && !pause && level) if ( !recordInputs() ) stopRecordInputs();
31759 
31760     if ((!pause && ingame == 1) || alwaysupdate)
31761     {
31762         execute_updatescripts();
31763     }
31764 
31765     newtime = 0;
31766     if(!pause)
31767     {
31768         if(ingame == 1 || inScreen)
31769         {
31770             execute_keyscripts();
31771         }
31772 
31773         if((level_completed && !level->noslow && !tospeedup) || slowmotion.toggle > SLOW_MOTION_OFF)
31774         {
31775             if(slowmotion.duration == slowmotion.counter)
31776             {
31777                 newtime = time + interval;
31778             }
31779         }
31780         else
31781         {
31782             newtime = time + interval;
31783         }
31784 
31785         slowmotion.counter++;
31786         if(slowmotion.counter == (slowmotion.duration + 1))
31787         {
31788             slowmotion.counter = 0;
31789             if(slowmotion.toggle > SLOW_MOTION_ON)
31790             {
31791                 slowmotion.duration = slowmotion.toggle;
31792             }
31793         }
31794         if(newtime > time + 100)
31795         {
31796             newtime = time + 100;
31797         }
31798 
31799         while(time < newtime)
31800         {
31801             if(ingame == 1)
31802             {
31803                 update_scroller();
31804                 if(!freezeall)
31805                 {
31806                     if(level->settime > 0 || (level->type != 2 && !player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent))
31807                     {
31808                         if(timeleft > 0)
31809                         {
31810                             --timeleft;
31811                         }
31812                         else if((level->settime > 0 && !player[0].joining && !player[1].joining && !player[2].joining && !player[3].joining) ||
31813                                 (((!noshare && credits < 1) || (noshare && player[0].credits < 1 && player[1].credits < 1 && player[2].credits < 1 && player[3].credits < 1))
31814                                  && !player[0].joining && !player[1].joining && !player[2].joining && !player[3].joining )
31815                                )
31816                         {
31817                             time_over();
31818                         }
31819                     }
31820                 }
31821                 update_scrolled_bg();
31822                 if(level->type != 2)
31823                 {
31824                     updatestatus();
31825                 }
31826             }
31827             if(ingame == 1 || inScreen)
31828             {
31829                 update_ents();
31830             }
31831             ++time;
31832         }
31833 
31834     }
31835 
31836     /************ gfx queueing ************/
31837 
31838     clearscreen(vscreen);
31839 
31840     if(ingame == 1 && !pause)
31841     {
31842         draw_scrolled_bg();
31843         if(level->type != 2)
31844         {
31845             predrawstatus();
31846         }
31847         if(level->type != 2)
31848         {
31849             drawstatus();
31850         }
31851         draw_textobjs();
31852     }
31853 
31854     if(!ingame)
31855     {
31856         if(background)
31857         {
31858             spriteq_add_screen(0, 0, MIN_INT, background, NULL, 0);
31859         }
31860     }
31861 
31862     // entity sprites queueing
31863     if(ingame == 1 || inScreen)
31864         if(!pause)
31865         {
31866             display_ents();
31867         }
31868 
31869     /************ updated script  ************/
31870     if(ingame == 1 || alwaysupdate)
31871     {
31872         execute_updatedscripts();
31873     }
31874 
31875     // 2011/10/22 UT: move pause menu logic here
31876     if(ingame == 1 && !pause && !nopause &&
31877             ((player[0].ent && (player[0].newkeys & FLAG_START)) ||
31878              (player[1].ent && (player[1].newkeys & FLAG_START)) ||
31879              (player[2].ent && (player[2].newkeys & FLAG_START)) ||
31880              (player[3].ent && (player[3].newkeys & FLAG_START)))
31881       )
31882     {
31883         if ( !(goto_mainmenu_flag&1) )
31884         {
31885             sound_pause_music(1);
31886             sound_pause_sample(1);
31887             sound_play_sample(SAMPLE_PAUSE, 0, savedata.effectvol, savedata.effectvol, 100);
31888             pausemenu();
31889             return;
31890         }
31891     }
31892     if( ingame == 1 && (goto_mainmenu_flag&1) )
31893     {
31894         backto_mainmenu();
31895         return;
31896     }
31897 
31898     /********** update screen **************/
31899 
31900     spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0); // notice, always draw sprites at the very end of other methods
31901 
31902     if(pause != 2 && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT))
31903     {
31904         screenshot(vscreen, getpal, 1);
31905     }
31906 
31907     // Debug stuff, should not appear on screenshot
31908     if(debug_time == 0xFFFFFFFF)
31909     {
31910         debug_time = time + GAME_SPEED * 5;
31911     }
31912     if(time < debug_time && debug_msg[0])
31913     {
31914         if( debug_xy_msg.x >= 0 && debug_xy_msg.y >= 0 )
31915         {
31916             if ( !debug_xy_msg.font_index ) debug_xy_msg.font_index = 0;
31917             screen_printf(vscreen, debug_xy_msg.x, debug_xy_msg.y, debug_xy_msg.font_index, debug_msg);
31918         } else screen_printf(vscreen, 0, videomodes.vRes-fontheight(0), 0, debug_msg);
31919     }
31920     else
31921     {
31922         debug_msg[0] = 0;
31923         debug_xy_msg.x = -1;
31924         debug_xy_msg.y = -1;
31925 #ifdef DEBUG_MODE
31926         if(level->pos)
31927         {
31928             debug_printf("Position: %i, width: %i, spawn: %i, offsets: %i/%i", level->pos, level->width, current_spawn, level->quake, gfx_y_offset);
31929         }
31930 #endif
31931     }
31932 
31933     if(usevwait)
31934     {
31935         vga_vwait();
31936     }
31937     video_copy_screen(vscreen);
31938     spriteq_clear();
31939 
31940     check_music();
31941     sound_update_music();
31942 }
31943 
31944 
31945 
31946 
31947 // ----------------------------------------------------------------------
31948 /* Plombo 9/4/2010: New function that can use brightness/gamma correction
31949  * independent from the global palette on platforms where it's available.
31950  * Hardware accelerated brightness/gamma correction is available on Wii and
31951  * OpenGL platforms using TEV and GLSL, respectively. Returns 1 on success, 0 on
31952  * error. */
set_color_correction(int gm,int br)31953 int set_color_correction(int gm, int br)
31954 {
31955 #if WII || SDL
31956     video_set_color_correction(gm, br);
31957     return 1;
31958 #else
31959     if(screenformat == PIXEL_8)
31960     {
31961         palette_set_corrected(pal, savedata.gamma, savedata.gamma, savedata.gamma, savedata.brightness, savedata.brightness, savedata.brightness);
31962         return 1;
31963     }
31964     else
31965     {
31966         return 0;
31967     }
31968 #endif
31969 }
31970 
31971 // Simple palette fade / vscreen fade
fade_out(int type,int speed)31972 void fade_out(int type, int speed)
31973 {
31974     int i, j = 0;
31975     int b, g = 0;
31976     u32 interval = 0;
31977     int current = speed ? speed : fade;
31978     s_screen *fbuffer = NULL;
31979     s_drawmethod dm = plainmethod;
31980     dm.alpha = 6;
31981 
31982     for(i = 0, j = 0; j < 64; )
31983     {
31984         while(j <= i && j < 64)
31985         {
31986             if(!type || type == 1)
31987             {
31988                 b = ((savedata.brightness + 256) * (64 - j) / 64) - 256;
31989                 g = 256 - ((savedata.gamma + 256) * (64 - j) / 64);
31990                 vga_vwait();
31991                 if(!set_color_correction(g, b))
31992                 {
31993                     if(!fbuffer)
31994                     {
31995                         fbuffer = allocscreen(vscreen->width, vscreen->height, vscreen->pixelformat);
31996                         copyscreen(fbuffer, vscreen);
31997                     }
31998                     //255 + alpha 6 is actually half blend, so use 254 instead
31999                     dm.channelr = dm.channelg = dm.channelb = 254 * (64 - j) / 64;
32000                     clearscreen(vscreen);
32001                     putscreen(vscreen, fbuffer, 0, 0, &dm);
32002                 }
32003             }
32004             j++;
32005             if(!type || type == 1)
32006             {
32007                 video_copy_screen(vscreen);
32008             }
32009         }
32010         if(!type || type == 2)
32011         {
32012             sound_update_music();
32013             if(!musicoverlap)
32014             {
32015                 sound_volume_music(savedata.musicvol * (64 - j) / 64, savedata.musicvol * (64 - j) / 64);
32016             }
32017         }
32018         interval = timer_getinterval(current);
32019         if(interval > current)
32020         {
32021             interval = current / 60;
32022         }
32023         if(interval > current / 4)
32024         {
32025             interval = current / 4;
32026         }
32027         i += interval;
32028     }
32029 
32030     if(!type || type == 2)
32031     {
32032         if(!musicoverlap)
32033         {
32034             sound_close_music();
32035         }
32036     }
32037 
32038     if(!type || type == 1)
32039     {
32040         clearscreen(vscreen);
32041         video_copy_screen(vscreen);
32042         vga_vwait();
32043         //the black screen, so we return to normal palette
32044         set_color_correction(savedata.gamma, savedata.brightness);
32045     }
32046 
32047     if(fbuffer)
32048     {
32049         freescreen(&fbuffer);
32050     }
32051 }
32052 
32053 
32054 
apply_controls()32055 void apply_controls()
32056 {
32057     int p;
32058 
32059     for(p = 0; p < 4; p++)
32060     {
32061         control_setkey(playercontrolpointers[p], FLAG_ESC,        CONTROL_ESC);
32062         control_setkey(playercontrolpointers[p], FLAG_MOVEUP,     savedata.keys[p][SDID_MOVEUP]);
32063         control_setkey(playercontrolpointers[p], FLAG_MOVEDOWN,   savedata.keys[p][SDID_MOVEDOWN]);
32064         control_setkey(playercontrolpointers[p], FLAG_MOVELEFT,   savedata.keys[p][SDID_MOVELEFT]);
32065         control_setkey(playercontrolpointers[p], FLAG_MOVERIGHT,  savedata.keys[p][SDID_MOVERIGHT]);
32066         control_setkey(playercontrolpointers[p], FLAG_ATTACK,     savedata.keys[p][SDID_ATTACK]);
32067         control_setkey(playercontrolpointers[p], FLAG_ATTACK2,    savedata.keys[p][SDID_ATTACK2]);
32068         control_setkey(playercontrolpointers[p], FLAG_ATTACK3,    savedata.keys[p][SDID_ATTACK3]);
32069         control_setkey(playercontrolpointers[p], FLAG_ATTACK4,    savedata.keys[p][SDID_ATTACK4]);
32070         control_setkey(playercontrolpointers[p], FLAG_JUMP,       savedata.keys[p][SDID_JUMP]);
32071         control_setkey(playercontrolpointers[p], FLAG_SPECIAL,    savedata.keys[p][SDID_SPECIAL]);
32072         control_setkey(playercontrolpointers[p], FLAG_START,      savedata.keys[p][SDID_START]);
32073         control_setkey(playercontrolpointers[p], FLAG_SCREENSHOT, savedata.keys[p][SDID_SCREENSHOT]);
32074     }
32075 }
32076 
32077 
32078 
32079 // ----------------------------------------------------------------------
32080 
display_credits()32081 void display_credits()
32082 {
32083     u32 finishtime = time + 10 * GAME_SPEED;
32084     int done = 0;
32085     int s = videomodes.vShift / 2 + 3;
32086     int v = (videomodes.vRes - videomodes.vShift) / 24;
32087     int m = 0;
32088     int h = videomodes.hRes / 2;
32089     int col1 = h - fontmonowidth(0) * 16;
32090     int col2 = h + fontmonowidth(0) * 4;
32091 
32092     if(savedata.logo != 1)
32093     {
32094         return;
32095     }
32096     fade_out(0, 0);
32097 
32098     unload_level();
32099 
32100     bothnewkeys = 0;
32101 
32102     while(!done)
32103     {
32104         m = 2;
32105 
32106         font_printf(_strmidx(2, "Credits"), s,  2, 0, "Credits");
32107         font_printf(_strmidx(1, "Beats Of Rage"), s + v * m,  1, 0, "Beats Of Rage"); ++m;
32108         font_printf(_strmidx(0, "Senile Team"), s + v * m,  0, 0, "Senile Team"); m+=2;
32109 
32110         font_printf(_strmidx(1, "OpenBOR"), s + v * m,  1, 0, "OpenBOR"); ++m;
32111         font_printf(_strmidx(0, "SX"), s + v * m,  0, 0, "SX"); ++m;
32112         font_printf(col1,  s + v * m,  0, 0, "CGRemakes");
32113         font_printf(col2, s + v * m,  0, 0, "Fugue"); ++m;
32114         font_printf(col1,  s + v * m,  0, 0, "uTunnels");
32115         font_printf(col2, s + v * m,  0, 0, "Kirby"); ++m;
32116         font_printf(col1,  s + v * m,  0, 0, "LordBall");
32117         font_printf(col2, s + v * m,  0, 0, "Tails"); ++m;
32118         font_printf(col1,  s + v * m, 0, 0, "KBAndressen");
32119         font_printf(col2, s + v * m, 0, 0, "Damon Caskey"); ++m;
32120         font_printf(col1,  s + v * m, 0, 0, "Plombo");
32121         font_printf(col2, s + v * m, 0, 0, "Orochi_X");  ++m;
32122         font_printf(col1, s + v * m, 0, 0, "White Dragon");  ++m;
32123 
32124         font_printf(_strmidx(1, "Ports"), s + v * m,  1, 0, "Ports"); ++m;
32125         font_printf(col1,  s + v * m, 0, 0, "PSP/Linux/OSX");
32126         font_printf(col2, s + v * m, 0, 0, "SX"); ++m;
32127         font_printf(col1,  s + v * m, 0, 0, "OpenDingux");
32128         font_printf(col2, s + v * m, 0, 0, "Shin-NiL"); ++m;
32129         font_printf(col1,  s + v * m, 0, 0, "Windows");
32130         font_printf(col2, s + v * m, 0, 0, "SX & Nazo"); ++m;
32131         font_printf(col1,  s + v * m, 0, 0, "GamePark");
32132         font_printf(col2, s + v * m, 0, 0, "SX & Lemon"); ++m;
32133         font_printf(col1,  s + v * m, 0, 0, "DreamCast");
32134         font_printf(col2, s + v * m, 0, 0, "SX & Neill Corlett"); ++m;
32135         font_printf(col1,  s + v * m, 0, 0, "Wii");
32136         font_printf(col2, s + v * m, 0, 0, "SX & Plombo"); ++m;
32137         font_printf(col1,  s + v * m, 0, 0, "Android");
32138         font_printf(col2, s + v * m, 0, 0, "uTunnels & CRxTRDude"); ++m;
32139 
32140         font_printf(_strmidx(1, "Menu Design"), s + v * m,  1, 0, "Menu Design"); ++m;
32141         font_printf(col1, s + v * m,  0, 0, "SX");
32142         font_printf(col2, s + v * m, 0, 0, "Fightn Words");
32143 
32144         update(2, 0);
32145 
32146         done |= (time > finishtime);
32147         done |= (bothnewkeys & (FLAG_START + FLAG_ESC));
32148     }
32149     fade = 75;
32150     fade_out(0, 0);
32151 }
32152 
32153 
shutdown(int status,char * msg,...)32154 void shutdown(int status, char *msg, ...)
32155 {
32156     char buf[1024] = "";
32157     va_list arglist;
32158     int i;
32159 
32160     static int shuttingdown = 0;
32161 
32162     if(shuttingdown)
32163     {
32164         return;
32165     }
32166 
32167     shuttingdown = 1;
32168 
32169     //printf("savedata.logo %d\n", savedata.logo);
32170 
32171     va_start(arglist, msg);
32172     vsprintf(buf, msg, arglist);
32173     va_end(arglist);
32174 
32175     if(!disablelog)
32176     {
32177         switch(status)
32178         {
32179         case 0:
32180             printf("\n************ Shutting Down ************\n\n");
32181             break;
32182         default:
32183             printf("\n********** An Error Occurred **********"
32184                    "\n*            Shutting Down            *\n\n");
32185             break;
32186         }
32187     }
32188 
32189     if(!disablelog)
32190     {
32191         printf("%s", buf);
32192     }
32193 
32194 
32195     getRamStatus(BYTES);
32196     savesettings();
32197 
32198     enginecreditsScreen = 1;		//entry point for the engine credits screen.
32199 
32200     if(status != 2)
32201     {
32202         display_credits();
32203     }
32204 
32205     if(startup_done)
32206     {
32207         enginecreditsScreen = 0; //once the engine credits is done, disable flag.
32208         term_videomodes();
32209     }
32210 
32211     if(!disablelog)
32212     {
32213         printf("Release level data");
32214     }
32215     if (startup_done)
32216     {
32217         unload_levelorder();
32218     }
32219     if(!disablelog)
32220     {
32221         printf("...........\n");
32222     }
32223     if(startup_done)
32224     {
32225         unload_level();
32226     }
32227     if(!disablelog)
32228     {
32229         printf("Done!\n\n");
32230     }
32231 
32232     if(!disablelog)
32233     {
32234         printf("Release graphics data");
32235     }
32236     if(!disablelog)
32237     {
32238         printf("..");
32239     }
32240     if(startup_done)
32241     {
32242         freescreen(&vscreen);    // allocated by init_videomodes
32243     }
32244     if(startup_done && pixelformat == PIXEL_x8) for(i = 0; i < MAX_BLENDINGS; i++)
32245         {
32246             free(blendtables[i]);
32247         }
32248     if(!disablelog)
32249     {
32250         printf("..");
32251     }
32252     if(startup_done)
32253     {
32254         freescreen(&background);
32255     }
32256     if(!disablelog)
32257     {
32258         printf("..");
32259     }
32260 #ifdef CACHE_BACKGROUNDS
32261     if(startup_done) for(i = 0; i < MAX_CACHED_BACKGROUNDS; i++)
32262         {
32263             freescreen(&bg_cache[i]);
32264         }
32265     if(!disablelog)
32266     {
32267         printf("..");
32268     }
32269 #endif
32270     if(startup_done)
32271     {
32272         freesprites();
32273     }
32274     if(!disablelog)
32275     {
32276         printf("..");
32277     }
32278     if(startup_done)
32279     {
32280         unload_all_fonts();
32281     }
32282     if(!disablelog)
32283     {
32284         printf("\tDone!\n");
32285     }
32286 
32287 
32288     if(!disablelog)
32289     {
32290         printf("Release game data............\n\n");
32291     }
32292 
32293     if(startup_done)
32294     {
32295         free_ents();
32296     }
32297     if(startup_done)
32298     {
32299         free_models();
32300     }
32301     if(startup_done)
32302     {
32303         free_modelcache();
32304     }
32305     if(startup_done)
32306     {
32307         clear_scripts();
32308     }
32309     if(!disablelog)
32310     {
32311         printf("\nRelease game data............\tDone!\n");
32312     }
32313 
32314     if(!disablelog)
32315     {
32316         printf("Release timer................");
32317     }
32318     if(startup_done)
32319     {
32320         borTimerExit();
32321     }
32322     if(!disablelog)
32323     {
32324         printf("\tDone!\n");
32325     }
32326 
32327     if(!disablelog)
32328     {
32329         printf("Release input hardware.......");
32330     }
32331     if(startup_done)
32332     {
32333         control_exit();
32334     }
32335     if(!disablelog)
32336     {
32337         printf("\tDone!\n");
32338     }
32339 
32340     if(!disablelog)
32341     {
32342         printf("Release sound system.........");
32343     }
32344     if(startup_done)
32345     {
32346         sound_exit();
32347     }
32348     if(!disablelog)
32349     {
32350         printf("\tDone!\n");
32351     }
32352 
32353     if(!disablelog)
32354     {
32355         printf("Release FileCaching System...");
32356     }
32357     if(startup_done)
32358     {
32359         pak_term();
32360     }
32361     if(!disablelog)
32362     {
32363         printf("\tDone!\n");
32364     }
32365 
32366     if(modelcmdlist)
32367     {
32368         freeCommandList(modelcmdlist);    // moved here because list is not initialized if shutdown is initiated from inside the menu
32369     }
32370     if(modelstxtcmdlist)
32371     {
32372         freeCommandList(modelstxtcmdlist);
32373     }
32374     if(levelcmdlist)
32375     {
32376         freeCommandList(levelcmdlist);
32377     }
32378     if(levelordercmdlist)
32379     {
32380         freeCommandList(levelordercmdlist);
32381     }
32382 
32383     freeModelList();
32384     if(savelevel)
32385     {
32386         free(savelevel);
32387     }
32388     freefilenamecache();
32389     ob_termtrans();
32390 
32391     // free input recorder
32392     free_input_recorder();
32393 
32394     if(!disablelog)
32395     {
32396         printf("\n**************** Done *****************\n\n");
32397     }
32398 
32399     if(!disablelog)
32400     {
32401         printf("%s", buf);
32402     }
32403 #ifdef DEBUG
32404     sysassert(status == 0); // this way we can get a backtrace.
32405 #endif
32406 
32407     shuttingdown = 0;
32408     exit(status);
32409 }
32410 
32411 
32412 #if DC
guistartup()32413 void guistartup()
32414 {
32415     int i;
32416 
32417     if(!font_load(0, "menu/font1", packfile, 0))
32418     {
32419         shutdown(1, "Unable to load font #1!\n");
32420     }
32421     if(!font_load(1, "menu/font2", packfile, 0))
32422     {
32423         shutdown(1, "Unable to load font #2!\n");
32424     }
32425     if(!font_load(2, "menu/font3", packfile, 0))
32426     {
32427         shutdown(1, "Unable to load font #3!\n");
32428     }
32429 
32430 
32431     borTimerInit();
32432 
32433     control_init(2);
32434     apply_controls();
32435 
32436     init_videomodes(0);
32437     if(!video_set_mode(videomodes))
32438     {
32439         shutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
32440     }
32441 
32442     for(i = 0; i < 256; i++)
32443     {
32444         neontable[i] = i;
32445     }
32446 }
32447 #endif
32448 
startup()32449 void startup()
32450 {
32451     int i;
32452 
32453     printf("FileCaching System Init......\t");
32454     if(pak_init())
32455     {
32456         printf("Enabled\n");
32457     }
32458     else
32459     {
32460         printf("Disabled\n");
32461     }
32462 
32463 #if PSP
32464     if(savedata.pspcpuspeed < 0)
32465     {
32466         savedata.pspcpuspeed = 2;
32467     }
32468     if(savedata.pspcpuspeed > 2)
32469     {
32470         savedata.pspcpuspeed = 0;
32471     }
32472     switch(savedata.pspcpuspeed)
32473     {
32474     case 0:
32475         scePowerSetClockFrequency(222, 222, 111);
32476         break;
32477     case 1:
32478         scePowerSetClockFrequency(266, 266, 133);
32479         break;
32480     case 2:
32481         scePowerSetClockFrequency(333, 333, 166);
32482         break;
32483     }
32484 #endif
32485 
32486     ob_inittrans();
32487     loadHighScoreFile();
32488     clearSavedGame();
32489 
32490     init_videomodes(1);
32491     if(!video_set_mode(videomodes))
32492     {
32493         shutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
32494     }
32495 
32496     if(pixelformat == PIXEL_8)
32497     {
32498         standard_palette(1);
32499     }
32500 
32501     printf("Loading menu.txt.............\t");
32502     load_menu_txt();
32503     printf("Done!\n");
32504 
32505     printf("Loading fonts................\t");
32506     load_all_fonts();
32507     printf("Done!\n");
32508 
32509     printf("Timer init...................\t");
32510     borTimerInit();
32511     printf("Done!\n");
32512 
32513     printf("Initialize Sound..............\t");
32514     if(savedata.usesound && sound_init(12))
32515     {
32516         if(load_special_sounds())
32517         {
32518             printf("Done!\n");
32519         }
32520         else
32521         {
32522             printf("\n");
32523         }
32524         if(!sound_start_playback(savedata.soundbits, savedata.soundrate))
32525         {
32526             printf("Warning: can't play sound at %u Hz!\n", savedata.soundrate);
32527         }
32528         SB_setvolume(SB_MASTERVOL, 15);
32529         SB_setvolume(SB_VOICEVOL, savedata.soundvol);
32530     }
32531     else
32532     {
32533         shutdown(1, "Unable to Initialize Sound.\n");
32534     }
32535 
32536     // init. input recorder
32537     init_input_recorder();
32538 
32539     printf("Loading sprites..............\t");
32540     load_special_sprites();
32541     printf("Done!\n");
32542 
32543     printf("Loading level order..........\t");
32544     load_levelorder();
32545     printf("Done!\n");
32546 
32547     printf("Loading model constants......\t");
32548     load_model_constants();
32549     printf("Done!\n");
32550 
32551     printf("Loading script settings......\t");
32552     load_script_setting();
32553     printf("Done!\n");
32554 
32555     printf("Loading scripts..............\t");
32556     load_scripts();
32557     printf("Done!\n");
32558 
32559     printf("Loading models...............\n\n");
32560     load_models();
32561 
32562     printf("Object engine init...........\t");
32563     if(!alloc_ents())
32564     {
32565         shutdown(1, "Not enough memory for game objects!\n");
32566     }
32567     printf("Done!\n");
32568 
32569     printf("Input init...................\t");
32570     control_init(savedata.usejoy);
32571     apply_controls();
32572     printf("Done!\n");
32573 
32574 #ifdef CACHE_BACKGROUNDS
32575     printf("Caching backgrounds..........\t");
32576     cache_all_backgrounds();
32577     printf("Done!\n");
32578 #endif
32579 
32580     printf("Create blending tables.......\t");
32581     if(pixelformat == PIXEL_x8)
32582     {
32583         create_blend_tables_x8(blendtables);
32584     }
32585 
32586     for(i = 0; i < MAX_PAL_SIZE / 4; i++)
32587     {
32588         neontable[i] = i;
32589     }
32590     printf("Done!\n");
32591 
32592     if(savedata.logo++ > 10)
32593     {
32594         savedata.logo = 0;
32595     }
32596 
32597     printf("Save settings so far........\t");
32598     savesettings();
32599     printf("Done!\n");
32600 
32601     startup_done = 1;
32602 
32603     printf("\n\n");
32604 
32605 }
32606 
32607 
32608 
32609 // ----------------------------------------------------------------------------
32610 
32611 
32612 // Returns 0 on error, -1 on escape
playgif(char * filename,int x,int y,int noskip)32613 int playgif(char *filename, int x, int y, int noskip)
32614 {
32615     anigif_info *info = calloc(1, sizeof(*info));
32616     s_screen *backbuffer = NULL;
32617     s_screen *tempbg = background;
32618     int result = 1;
32619     u32 synctosound;
32620     u32 lasttime;
32621     u32 milliseconds;
32622     u32 temptime, tempnewtime; // temporary patch for ingame gif play
32623 
32624     synctosound = (sound_getinterval() != 0xFFFFFFFF);
32625     temptime = time;
32626     tempnewtime = newtime;
32627     time = 0;
32628     milliseconds = 0;
32629     lasttime = 0;
32630     background = NULL;
32631 
32632     if(!(result = anigif_open(filename, packfile, info)))
32633     {
32634         goto playgif_end;
32635     }
32636 
32637     while(!info->done)
32638     {
32639         if(milliseconds >= info->info[0].nextframe)
32640         {
32641             anigif_decode_frame(info);
32642         }
32643         if(!(backbuffer = anigif_getbuffer(info)))
32644         {
32645             break;
32646         }
32647         spriteq_add_screen(x, y, 0, backbuffer, NULL, 0);
32648         if(info->frame == 0)
32649         {
32650             vga_vwait();
32651             if(screenformat == PIXEL_8)
32652             {
32653                 palette_set_corrected(backbuffer->palette, savedata.gamma, savedata.gamma, savedata.gamma, savedata.brightness, savedata.brightness, savedata.brightness);
32654             }
32655             update(0, 0);
32656         }
32657         else
32658         {
32659             update(0, 1);
32660         }
32661 
32662         if(synctosound)
32663         {
32664             milliseconds += sound_getinterval();
32665         }
32666         else
32667         {
32668             milliseconds += (time - lasttime) * 1000 / GAME_SPEED;
32669         }
32670 
32671         lasttime = time;
32672 
32673         if(!noskip && (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)))
32674         {
32675             result = -1;
32676             break;
32677         }
32678     }
32679 
32680 
32681 playgif_end:
32682     anigif_close(info);
32683     free(info);
32684     time = temptime;
32685     newtime = tempnewtime;
32686     background = tempbg;
32687     standard_palette(1);
32688 
32689     if(0 == result)
32690     {
32691         printf("\nWarning, an error occurred while playing animated gif file '%s'.\n", filename);
32692     }
32693     return result;
32694 
32695 }
32696 
32697 
32698 #ifdef WEBM
32699 // Returns 0 on error, -1 on escape
playwebm(const char * path,int noskip)32700 int playwebm(const char *path, int noskip)
32701 {
32702     int retval = 1;
32703     webm_context *ctx = NULL;
32704     yuv_video_mode info;
32705     s_screen *rgb_frame = NULL;
32706 
32707     ctx = webm_start_playback(path, savedata.musicvol);
32708     if(ctx == NULL) {retval=0; goto quit;}
32709 
32710     // set video output to YUV mode
32711     webm_get_video_info(ctx, &info);
32712     int status = video_setup_yuv_overlay(&info);
32713     if(!status) {retval=0; goto quit;}
32714 
32715     // allocate s_screen for screenshot capture
32716     yuv_init(2);
32717     rgb_frame = allocscreen(info.width, info.height, PIXEL_16);
32718     if(!rgb_frame) {retval=0; goto quit;}
32719 
32720     u64 start_time = timer_uticks();
32721     u64 next_frame_time = 0;
32722     yuv_frame *frame = NULL;
32723 
32724     while(1)
32725     {
32726         inputrefresh(playrecstatus->status);
32727         if(!noskip && (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)))
32728         {
32729             retval = -1;
32730             yuv_frame_destroy(frame);
32731             break;
32732         }
32733         else if(frame && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT))
32734         {
32735             yuv_to_rgb(frame, rgb_frame);
32736             screenshot(rgb_frame, NULL, 0);
32737         }
32738 
32739         u64 time_passed = timer_uticks() - start_time;
32740 
32741         if(next_frame_time <= time_passed)
32742         {
32743             // display the current frame
32744             if(frame)
32745             {
32746                 video_display_yuv_frame();
32747                 yuv_frame_destroy(frame);
32748             }
32749 
32750             // prepare the next frame for display
32751             frame = webm_get_next_frame(ctx);
32752             if(frame == NULL) break;
32753             video_prepare_yuv_frame(frame);
32754             next_frame_time = frame->timestamp / 1000;
32755         }
32756         else usleep(next_frame_time - time_passed);
32757     }
32758 
32759 quit:
32760     if(ctx) webm_close(ctx);
32761     if(rgb_frame) freescreen(&rgb_frame);
32762     yuv_clear();
32763     video_set_mode(videomodes);
32764     return retval;
32765 }
32766 #endif
32767 
32768 
32769 
playscene(char * filename)32770 void playscene(char *filename)
32771 {
32772     char *buf;
32773     size_t size;
32774     int pos;
32775     char *command = NULL;
32776     char videofile[256];
32777     int x = 0, y = 0, skipone = 0, noskip = 0, i;
32778     int closing = 0, status;
32779 
32780     ArgList arglist;
32781     char argbuf[MAX_ARG_LEN + 1] = "";
32782 
32783     // Read file
32784     if(buffer_pakfile(filename, &buf, &size) != 1)
32785     {
32786         return;
32787     }
32788 
32789     currentScene = filename;
32790 
32791     // Now interpret the contents of buf line by line
32792     pos = 0;
32793     while(buf[pos])
32794     {
32795         ParseArgs(&arglist, buf + pos, argbuf);
32796         command = GET_ARG(0);
32797         if(command[0])
32798         {
32799             if(!closing && stricmp(command, "music") == 0)
32800             {
32801                 music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
32802             }
32803             else if(!closing && stricmp(command, "animation") == 0)
32804             {
32805                 strcpy(videofile, GET_ARG(1));
32806                 x = GET_INT_ARG(2);
32807                 y = GET_INT_ARG(3);
32808                 skipone = GET_INT_ARG(4);
32809                 noskip = GET_INT_ARG(5);
32810                 status = playgif(videofile, x, y, noskip);
32811                 if(status == -1 && !skipone)
32812                 {
32813                     closing = 1;
32814                 }
32815             }
32816             else if(!closing && stricmp(command, "video") == 0)
32817             {
32818 #ifdef WEBM
32819                 strcpy(videofile, GET_ARG(1));
32820                 skipone = GET_INT_ARG(2);
32821                 noskip = GET_INT_ARG(3);
32822                 status = playwebm(videofile, noskip);
32823                 if(status == -1 && !skipone)
32824                 {
32825                     closing = 1;
32826                 }
32827                 else if(status == 0)
32828                 {
32829                     printf("An error occurred when trying to play the video %s\n", videofile);
32830                 }
32831 #else
32832                 printf("Skipping video %s; WebM playback not supported on this platform\n");
32833 #endif
32834             }
32835             else if(stricmp(command, "silence") == 0)
32836             {
32837                 sound_close_music();
32838             }
32839         }
32840         // Go to next non-blank line
32841         pos += getNewLineStart(buf + pos);
32842     }
32843     if(buf != NULL)
32844     {
32845         free(buf);
32846         buf = NULL;
32847     }
32848     currentScene = NULL;
32849     for(i = 0; i < MAX_PLAYERS; i++)
32850     {
32851         player[i].disablekeys = player[i].newkeys = player[i].playkeys = 0;
32852     }
32853 }
32854 
32855 
32856 
32857 
32858 // ----------------------------------------------------------------------------
32859 
32860 
32861 
32862 
gameover()32863 void gameover()
32864 {
32865     int done = 0;
32866     char tmpBuff[128] = {""};
32867 
32868     music("data/music/gameover", 0, 0);
32869 
32870     time = 0;
32871     gameOver = 1;
32872 
32873     if(custScenes != NULL)
32874     {
32875         strcpy(tmpBuff, custScenes);
32876         strncat(tmpBuff, "gameover.txt", 12);
32877         if(testpackfile(tmpBuff, packfile) >= 0)
32878         {
32879             playscene(tmpBuff);
32880             done = 1;
32881         }
32882     }
32883     else
32884     {
32885         if(testpackfile("data/scenes/gameover.txt", packfile) >= 0)
32886         {
32887             playscene("data/scenes/gameover.txt");
32888             done = 1;
32889         }
32890 
32891     }
32892 
32893     while(!done)
32894     {
32895         font_printf(_strmidx(3, Tr("GAME OVER")), 110 + videomodes.vShift, 3, 0, Tr("GAME OVER"));
32896         done |= (time > GAME_SPEED * 8 && !sound_query_music(NULL, NULL));
32897         done |= (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON));
32898         update(0, 0);
32899     }
32900     gameOver = 0;
32901 }
32902 
32903 
32904 
32905 
hallfame(int addtoscore)32906 void hallfame(int addtoscore)
32907 {
32908     int done = 0;
32909     int topten[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
32910     u32 score;
32911     char name[MAX_NAME_LEN + 1];
32912     int i, p, y;
32913     char tmpBuff[128] = {""};
32914     int col1 = -8;
32915     int col2 = 6;
32916 
32917     hallOfFame = 1;
32918 
32919     if(hiscorebg)
32920     {
32921         // New alternative background path for PSP
32922         if(custBkgrds != NULL)
32923         {
32924             strcpy(tmpBuff, custBkgrds);
32925             strncat(tmpBuff, "hiscore", 7);
32926             load_background(tmpBuff, 0);
32927         }
32928         else
32929         {
32930             load_cached_background("data/bgs/hiscore", 0);
32931         }
32932     }
32933 
32934     if(addtoscore)
32935     {
32936         for(p = 0; p < levelsets[current_set].maxplayers; p++)
32937         {
32938             if(player[p].score > savescore.highsc[9])
32939             {
32940                 savescore.highsc[9] = player[p].score;
32941                 strcpy(savescore.hscoren[9], player[p].name);
32942                 topten[9] = 1;
32943 
32944                 for(i = 8; i >= 0 && player[p].score > savescore.highsc[i]; i--)
32945                 {
32946                     score = savescore.highsc[i];
32947                     strcpy(name, savescore.hscoren[i]);
32948                     savescore.highsc[i] = player[p].score;
32949                     strcpy(savescore.hscoren[i], player[p].name);
32950                     topten[i] = 1;
32951                     savescore.highsc[i + 1] = score;
32952                     strcpy(savescore.hscoren[i + 1], name);
32953                     topten[i + 1] = 0;
32954                 }
32955             }
32956         }
32957         saveHighScoreFile();
32958     }
32959 
32960     time = 0;
32961 
32962     while(!done)
32963     {
32964         y = 56;
32965         if(!hiscorebg)
32966         {
32967             font_printf(_strmidx(3, Tr("Hall Of Fame")), y - fontheight(3) - 10 + videomodes.vShift, 3, 0, Tr("Hall Of Fame"));
32968         }
32969 
32970         for(i = 0; i < 10; i++)
32971         {
32972             font_printf(_colx(topten[i], col1), y + videomodes.vShift, topten[i], 0, "%2i.  %s", i + 1, savescore.hscoren[i]);
32973             font_printf(_colx(topten[i], col2), y + videomodes.vShift, topten[i], 0, (scoreformat ? "%09lu" : "%u"), savescore.highsc[i]);
32974             y += (videomodes.vRes - videomodes.vShift - 56 - 32) / 10; //font_heights[topten[i]] + 6;
32975         }
32976 
32977         update(0, 0);
32978         done |= (time > GAME_SPEED * 8);
32979         done |= (bothnewkeys & (FLAG_START + FLAG_ESC));
32980     }
32981     unload_background();
32982     hallOfFame = 0;
32983 }
32984 
32985 
32986 
32987 
32988 // Level completed, show bonus stuff
showcomplete(int num)32989 void showcomplete(int num)
32990 {
32991     int done = 0;
32992     int i, j, k;
32993     u32 clearbonus[4] = { 10000, 10000, 10000, 10000 };
32994     u32 lifebonus[4] = { 10000, 10000, 10000, 10000 };
32995     u32 rushbonus[4] = { 10000, 10000, 10000, 10000 };
32996     u32 nexttime = 0;
32997     u32 finishtime = 0;
32998     int chan = 0;
32999     char tmpBuff[128] = {""};
33000 
33001     showComplete = 1;
33002 
33003     if(completebg)
33004     {
33005         // New alternative background path for PSP
33006         if(custBkgrds != NULL)
33007         {
33008             strcpy(tmpBuff, custBkgrds);
33009             strncat(tmpBuff, "complete", 8);
33010             load_background(tmpBuff, 0);
33011         }
33012         else
33013         {
33014             load_cached_background("data/bgs/complete", 0);
33015         }
33016     }
33017 
33018     music("data/music/complete", 0, 0);
33019 
33020     for(i = 0; i < levelsets[current_set].maxplayers; i++)
33021     {
33022         if(rush[0] >= 1 && showrushbonus == 1)
33023         {
33024             rushbonus[i] = nomaxrushreset[i] * scbonuses[2];
33025         }
33026         if(scbonuses[3] == 1)
33027         {
33028             clearbonus[i] = num * scbonuses[0];
33029         }
33030         else
33031         {
33032             clearbonus[i] = scbonuses[0];
33033         }
33034         lifebonus[i] = player[i].lives * scbonuses[1];
33035     }
33036 
33037     update(0, 0);
33038 
33039     time = 0;
33040     while(!done)
33041     {
33042         if(!scomplete[5])
33043         {
33044             font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, Tr("Stage %i Complete!"), num);
33045         }
33046         else
33047         {
33048             font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, Tr("Stage"));
33049             font_printf(videomodes.hShift + scomplete[2], videomodes.vShift + scomplete[3], 3, 0, "%i", num);
33050             font_printf(videomodes.hShift + scomplete[4], videomodes.vShift + scomplete[5], 3, 0, Tr("Complete"));
33051         }
33052 
33053         font_printf(videomodes.hShift + cbonus[0], videomodes.vShift + cbonus[1], 0, 0, Tr("Clear Bonus"));
33054         for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
33055             {
33056                 font_printf(videomodes.hShift + cbonus[j], videomodes.vShift + cbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), clearbonus[i]);
33057             }
33058         font_printf(videomodes.hShift + lbonus[0], videomodes.vShift + lbonus[1], 0, 0, Tr("Life bonus"));
33059         for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
33060             {
33061                 font_printf(videomodes.hShift + lbonus[j], videomodes.vShift + lbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), lifebonus[i]);
33062             }
33063         if(rush[0] >= 1 && showrushbonus == 1)
33064         {
33065             font_printf(videomodes.hShift + rbonus[0], videomodes.vShift + rbonus[1], 0, 0, Tr("Rush Bonus"));
33066             for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
33067                 {
33068                     font_printf(videomodes.hShift + rbonus[j], videomodes.vShift + rbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), rushbonus[i]);
33069                 }
33070         }
33071         font_printf(videomodes.hShift + tscore[0], videomodes.vShift + tscore[1], 0, 0, Tr("Total Score"));
33072         for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0)
33073             {
33074                 font_printf(videomodes.hShift + tscore[j], videomodes.vShift + tscore[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), player[i].score);
33075             }
33076 
33077         while(time > nexttime)
33078         {
33079             if(!finishtime)
33080             {
33081                 finishtime = time + 4 * GAME_SPEED;
33082             }
33083 
33084             for(i = 0; i < levelsets[current_set].maxplayers; i++)
33085             {
33086                 if(player[i].lives > 0)
33087                 {
33088                     if(clearbonus[i] > 0)
33089                     {
33090                         addscore(i, 10);
33091                         clearbonus[i] -= 10;
33092                         finishtime = 0;
33093                     }
33094                     else if(lifebonus[i] > 0)
33095                     {
33096                         addscore(i, 10);
33097                         lifebonus[i] -= 10;
33098                         finishtime = 0;
33099                     }
33100                     else if(rush[0] >= 1 && showrushbonus == 1 && (rushbonus[i] > 0))
33101                     {
33102                         addscore(i, 10);
33103                         rushbonus[i] -= 10;
33104                         finishtime = 0;
33105                     }
33106                 }
33107             }
33108 
33109             if(!finishtime && !(nexttime & 15))
33110             {
33111                 sound_stop_sample(chan);
33112                 chan = sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol / 2, savedata.effectvol / 2, 100);
33113             }
33114             nexttime++;
33115         }
33116 
33117         if(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC))
33118         {
33119             done = 1;
33120         }
33121         if(finishtime && time > finishtime)
33122         {
33123             done = 1;
33124         }
33125 
33126         update(0, 0);
33127     }
33128 
33129     // Add remainder of score, incase player skips counter
33130     for(i = 0; i < levelsets[current_set].maxplayers; i++)
33131     {
33132         if(player[i].lives > 0)
33133         {
33134             if(rush[0] >= 1 && showrushbonus == 1)
33135             {
33136                 addscore(i, rushbonus[i]);
33137             }
33138             addscore(i, clearbonus[i]);
33139             addscore(i, lifebonus[i]);
33140         }
33141     }
33142     unload_background();
33143 
33144     showComplete = 0;
33145 }
33146 
savelevelinfo()33147 void savelevelinfo()
33148 {
33149     int i;
33150     s_set_entry *set = levelsets + current_set;
33151     s_savelevel *save = savelevel + current_set;
33152 
33153     save->flag = set->saveflag;
33154     // don't check flag here save all info, for simple logic
33155     for(i = 0; i < set->maxplayers; i++)
33156     {
33157         save->pLives[i] = player[i].lives;
33158         save->pCredits[i] = player[i].credits;
33159         save->pScores[i] = player[i].score;
33160         save->pSpawnhealth[i] = player[i].spawnhealth;
33161         save->pSpawnmp[i] = player[i].spawnmp;
33162         save->pWeapnum[i] = player[i].weapnum;
33163         save->pColourmap[i] = player[i].colourmap;
33164         strncpy(save->pName[i], player[i].name, MAX_NAME_LEN);
33165     }
33166     save->credits = credits;
33167     save->level = current_level;
33168     save->stage = current_stage;
33169     save->which_set = current_set;
33170     strncpy(save->dName, set->name, MAX_NAME_LEN);
33171     for(i = 0; i < sizeof(allowselect_args); i++) save->allowSelectArgs[i] = '\0'; // clear
33172     for(i = 0; i < sizeof(allowselect_args); i++) save->allowSelectArgs[i] = allowselect_args[i];
33173 }
33174 
33175 
33176 
playlevel(char * filename)33177 int playlevel(char *filename)
33178 {
33179     int i, type;
33180 
33181     kill_all();
33182 
33183     savelevelinfo(); // just in case we lose them after level is freed
33184 
33185     load_level(filename);
33186 
33187     if(!nosave)
33188     {
33189         saveGameFile();
33190         saveHighScoreFile();
33191         saveScriptFile();
33192     }
33193     nosave = 0;
33194 
33195     time = 0;
33196     nextplan = 0;
33197     stalker = NULL;
33198     firstplayer = NULL;
33199     type = level->type;
33200 
33201     // Fixes the start level executing last button bug
33202     for(i = 0; i < levelsets[current_set].maxplayers; i++)
33203     {
33204         if(player[i].lives > 0)
33205         {
33206             player[i].disablekeys = player[i].newkeys = player[i].playkeys = 0;
33207             player[i].weapnum = level->setweap;
33208             player[i].joining = 0;
33209             player[i].hasplayed = 1;
33210             spawnplayer(i);
33211             player[i].ent->rush.count.max = 0;
33212         }
33213     }
33214 
33215     //execute a script when level started
33216     if(Script_IsInitialized(&level_script))
33217     {
33218         Script_Execute(&level_script);
33219     }
33220     if(Script_IsInitialized(&(level->level_script)))
33221     {
33222         Script_Execute(&(level->level_script));
33223     }
33224 
33225     while(!endgame)
33226     {
33227         update(1, 0);
33228 
33229         if ( level->forcefinishlevel )
33230         {
33231             level_completed = 1;
33232             endgame |= 1;
33233             level->forcefinishlevel = 0;
33234         }
33235         if(level_completed)
33236         {
33237             endgame |= (!findent(TYPE_ENEMY) || level->type || findent(TYPE_ENDLEVEL));    // Ends when all enemies die or a bonus level
33238         }
33239     }
33240     //execute a script when level finished
33241     if(Script_IsInitialized(&endlevel_script))
33242     {
33243         Script_Execute(&endlevel_script);
33244     }
33245     if(Script_IsInitialized(&(level->endlevel_script)))
33246     {
33247         Script_Execute(&(level->endlevel_script));
33248     }
33249     if(!nofadeout)
33250     {
33251         fade_out(0, 0);
33252     }
33253 
33254     for(i = 0; i < levelsets[current_set].maxplayers; i++)
33255     {
33256         if(player[i].ent)
33257         {
33258             nomaxrushreset[i] = player[i].ent->rush.count.max;
33259             player[i].spawnhealth = player[i].ent->health;
33260             player[i].spawnmp = player[i].ent->mp;
33261         }
33262     }
33263 
33264     if(!musicoverlap)
33265     {
33266         sound_close_music();
33267     }
33268     sound_stopall_sample();
33269 
33270     unload_level();
33271 
33272     return (type == 2 && endgame != 2) || (player[0].lives > 0 || player[1].lives > 0 || player[2].lives > 0 || player[3].lives > 0); //4player
33273 }
33274 
33275 
spawnexample(int i)33276 static entity *spawnexample(int i)
33277 {
33278     entity *example;
33279     s_model *model = NULL;
33280     s_set_entry *set = levelsets + current_set;
33281     example = spawn((float)psmenu[i][0], (float)psmenu[i][1], 0, spdirection[i], NULL, -1, nextplayermodeln(NULL, i));
33282     strcpy(player[i].name, example->model->name);
33283     model = example->model;
33284 
33285     //player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmap(example->model, i - 1) : 0;
33286     // NOSAME X 2 // By White Dragon
33287     player[i].colourmap = 0;
33288     if ( colourselect && (set->nosame & 2) )
33289     {
33290         int nosamef = -1;
33291         int ii = 0;
33292         for(ii = 0; ii < set->maxplayers; ii++)
33293         {
33294             if ( i != ii && stricmp(player[i].name,player[ii].name) == 0 )
33295             {
33296                 if ( player[i].colourmap == player[ii].colourmap  )
33297                 {
33298                     if ( nosamef == player[ii].colourmap ) break; // avoid infinite loop
33299                     player[i].colourmap = nextcolourmap(model,player[i].colourmap);
33300                     ii = 0;
33301                     if ( nosamef == -1 ) nosamef = player[ii].colourmap;
33302                     continue;
33303                 }
33304             } else continue;
33305         }
33306     }
33307     // NOSAME X 2
33308 
33309     ent_set_colourmap(example, player[i].colourmap);
33310     return example;
33311 }
33312 
33313 // load saved select screen
load_select_screen_info(s_savelevel * save)33314 static void load_select_screen_info(s_savelevel *save)
33315 {
33316     int i = 0;
33317     ArgList arglist;
33318     char argbuf[MAX_ARG_LEN + 1] = "";
33319     char *filename = ""; // not used, just fused into GET_INT_ARG()
33320     char *command = ""; // not used, just fused into GET_INT_ARG()
33321 
33322     // load model_loads
33323     for(i = 0; i < save->selectLoadCount; i++)
33324     {
33325         s_model *tempmodel;
33326         command = GET_ARG(0);
33327 
33328         if(!command || !command[0]) continue;
33329         ParseArgs(&arglist, save->selectLoad[i], argbuf);
33330 
33331         tempmodel = findmodel(GET_ARG(1));
33332         if (tempmodel)
33333         {
33334             update_model_loadflag(tempmodel, GET_INT_ARG(2));
33335         }
33336     }
33337 
33338     ParseArgs(&arglist, save->selectMusic, argbuf);
33339     command = GET_ARG(0);
33340     if(command && command[0]) music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
33341 
33342     ParseArgs(&arglist, save->selectBackground, argbuf);
33343     command = GET_ARG(0);
33344     if(command && command[0]) load_background(GET_ARG(1), 1);
33345 
33346     return;
33347 }
33348 
selectplayer(int * players,char * filename,int useSavedGame)33349 int selectplayer(int *players, char *filename, int useSavedGame)
33350 {
33351     s_model *tempmodel;
33352     entity *example[4] = {NULL, NULL, NULL, NULL};
33353     int i;
33354     int exit = 0;
33355     int ready[MAX_PLAYERS] = {0, 0, 0, 0};
33356     int escape = 0;
33357     int defaultselect = 0;
33358     unsigned exitdelay = 0;
33359     int players_busy = 0;
33360     int players_ready = 0;
33361     char string[128] = {""};
33362     char *buf, *command;
33363     size_t size = 0;
33364     ptrdiff_t pos = 0;
33365     ArgList arglist;
33366     char argbuf[MAX_ARG_LEN + 1] = "";
33367     s_set_entry *set = levelsets + current_set;
33368     s_savelevel *save = savelevel + current_set;
33369     int load_count = 0, saved_select_screen = 0;
33370 
33371     savelevelinfo();
33372 
33373     selectScreen = 1;
33374     kill_all();
33375     if(allowselect_args[0] != 'a') reset_playable_list(1); // 'a' is the first char of allowselect, if there's 'a' then there is allowselect
33376     memset(player, 0, sizeof(*player) * 4);
33377 
33378     if(useSavedGame && save)
33379     {
33380         if (save->selectFlag)
33381         {
33382             load_select_screen_info(save);
33383             load_playable_list(save->allowSelectArgs);
33384             saved_select_screen = 1;
33385         }
33386     }
33387 
33388     //loadGameFile();
33389 
33390     for(i = 0; i < set->maxplayers; i++)
33391     {
33392         player[i].hasplayed = players[i];
33393     }
33394 
33395     if(filename && filename[0])
33396     {
33397         if(buffer_pakfile(filename, &buf, &size) != 1)
33398         {
33399             shutdown(1, "Failed to load player select file '%s'", filename);
33400         }
33401         while(pos < size)
33402         {
33403             ParseArgs(&arglist, buf + pos, argbuf);
33404             command = GET_ARG(0);
33405             if(command && command[0])
33406             {
33407                 if(stricmp(command, "music") == 0)
33408                 {
33409                     music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
33410                     // SAVE
33411                     multistrcatsp(save->selectMusic, command,GET_ARG(1),GET_ARG(2),GET_ARG(3),NULL);
33412                 }
33413                 else if(stricmp(command, "allowselect") == 0)
33414                 {
33415                     load_playable_list(buf + pos);
33416                     memcpy(&save->allowSelectArgs, &allowselect_args, sizeof(allowselect_args)); // SAVE
33417                 }
33418                 else if(stricmp(command, "background") == 0)
33419                 {
33420                     load_background(GET_ARG(1), 1);
33421                     // SAVE
33422                     multistrcatsp(save->selectBackground, command,GET_ARG(1),NULL);
33423                 }
33424                 else if(stricmp(command, "load") == 0)
33425                 {
33426                     tempmodel = findmodel(GET_ARG(1));
33427                     if (!tempmodel)
33428                     {
33429                         load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2));
33430                     }
33431                     else
33432                     {
33433                         update_model_loadflag(tempmodel, GET_INT_ARG(2));
33434                     }
33435                     // SAVE
33436                     if(load_count < MAX_SELECT_LOADS)
33437                     {
33438                         multistrcatsp(save->selectLoad[load_count], command,GET_ARG(1),GET_ARG(2),NULL);
33439                         load_count++;
33440                     }
33441                 }
33442                 else if(command && command[0])
33443                 {
33444                     printf("Command '%s' is not understood in file '%s'", command, filename);
33445                 }
33446             }
33447 
33448             pos += getNewLineStart(buf + pos);
33449         }
33450         save->selectLoadCount = load_count; // SAVE number of LOAD command
33451         save->selectFlag = 1;
33452 
33453         if(buf != NULL)
33454         {
33455             free(buf);
33456             buf = NULL;
33457         }
33458     }
33459     else // without select.txt
33460     {
33461         defaultselect = 1;
33462         if(!noshare)
33463         {
33464             credits = CONTINUES;
33465         }
33466         else for(i = 0; i < set->maxplayers; i++)
33467         {
33468             if(players[i])
33469             {
33470                 player[i].credits = CONTINUES;
33471             }
33472         }
33473 
33474         if(skipselect[0][0] || set->noselect)
33475         {
33476             for(i = 0; i < set->maxplayers; i++)
33477             {
33478                 if(!players[i])
33479                 {
33480                     continue;
33481                 }
33482                 strncpy(player[i].name, skipselect[i], MAX_NAME_LEN);
33483                 if(!creditscheat)
33484                 {
33485                     if(noshare)
33486                     {
33487                         --player[i].credits;
33488                     }
33489                     else
33490                     {
33491                         --credits;
33492                     }
33493                 }
33494                 player[i].lives = PLAYER_LIVES;
33495             }
33496             selectScreen = 0;
33497             return 1;
33498         }
33499 
33500         if (!saved_select_screen)
33501         {
33502             if(unlockbg && bonus)
33503             {
33504                 // New alternative background path for PSP
33505                 if(custBkgrds != NULL)
33506                 {
33507                     strcpy(string, custBkgrds);
33508                     strncat(string, "unlockbg", 8);
33509                     load_background(string, 1);
33510                 }
33511                 else
33512                 {
33513                     load_cached_background("data/bgs/unlockbg", 1);
33514                 }
33515             }
33516             else
33517             {
33518                 // New alternative background path for PSP
33519                 if(custBkgrds != NULL)
33520                 {
33521                     strncpy(string, custBkgrds, 128);
33522                     strncat(string, "select", 6);
33523                     load_background(string, 1);
33524                 }
33525                 else
33526                 {
33527                     load_cached_background("data/bgs/select", 1);
33528                 }
33529             }
33530             if(!music("data/music/menu", 1, 0))
33531             {
33532                 music("data/music/remix", 1, 0);
33533             }
33534         }
33535     }
33536 
33537     for(i = 0; i < set->maxplayers; i++)
33538     {
33539         if(players[i])
33540         {
33541             example[i] = spawnexample(i);
33542             player[i].playkeys = 0;
33543             if(defaultselect)
33544             {
33545                 player[i].lives = PLAYER_LIVES;
33546                 if(!creditscheat)
33547                 {
33548                     if(noshare)
33549                     {
33550                         --player[i].credits;
33551                     }
33552                     else
33553                     {
33554                         --credits;
33555                     }
33556                 }
33557             }
33558             else
33559             {
33560                 player[i].lives = savelevel[current_set].pLives[i];
33561                 player[i].credits = savelevel[current_set].pCredits[i];
33562                 player[i].score = savelevel[current_set].pScores[i];
33563             }
33564         }
33565     }
33566 
33567     time = 0;
33568     while(!(exit || escape))
33569     {
33570         players_busy = 0;
33571         players_ready = 0;
33572         for(i = 0; i < set->maxplayers; i++)
33573         {
33574             if(!ready[i])
33575             {
33576                 if(!player[i].hasplayed && (noshare || credits > 0) && (player[i].newkeys & FLAG_ANYBUTTON))
33577                 {
33578                     players[i] = player[i].hasplayed = 1;
33579                     //printf("%d %d %d\n", i, player[i].lives, immediate[i]);
33580                     if(noshare)
33581                     {
33582                         player[i].credits = CONTINUES;
33583                     }
33584 
33585                     if(!creditscheat)
33586                     {
33587                         if(noshare)
33588                         {
33589                             --player[i].credits;
33590                         }
33591                         else
33592                         {
33593                             --credits;
33594                         }
33595                     }
33596 
33597                     player[i].lives = PLAYER_LIVES;
33598                     example[i] = spawnexample(i);
33599                     player[i].playkeys = 0;
33600 
33601                     if(SAMPLE_BEEP >= 0)
33602                     {
33603                         sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
33604                     }
33605                 }
33606                 else if(player[i].newkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT) && example[i])
33607                 {
33608                     if(SAMPLE_BEEP >= 0)
33609                     {
33610                         sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
33611                     }
33612                     ent_set_model(example[i], ((player[i].newkeys & FLAG_MOVELEFT) ? prevplayermodeln : nextplayermodeln)(example[i]->model, i)->name, 0);
33613                     strcpy(player[i].name, example[i]->model->name);
33614                     player[i].colourmap = nextcolourmap(example[i]->model, i - 1);
33615                     ent_set_colourmap(example[i], player[i].colourmap);
33616                 }
33617                 // oooh pretty colors! - selectable color scheme for player characters
33618                 else if(player[i].newkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN) && colourselect && example[i])
33619                 {
33620                     player[i].colourmap = ((player[i].newkeys & FLAG_MOVEUP) ? nextcolourmap : prevcolourmap)(example[i]->model, player[i].colourmap);
33621                     ent_set_colourmap(example[i], player[i].colourmap);
33622                 }
33623                 else if((player[i].newkeys & FLAG_ANYBUTTON) && example[i])
33624                 {
33625                     if(SAMPLE_BEEP2 >= 0)
33626                     {
33627                         sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
33628                     }
33629                     // yay you picked me!
33630                     if(validanim(example[i], ANI_PICK))
33631                     {
33632                         ent_set_anim(example[i], ANI_PICK, 0);
33633                     }
33634                     example[i]->stalltime = time + GAME_SPEED * 2;
33635                     ready[i] = 1;
33636                 }
33637             }
33638             else if(ready[i] == 1)
33639             {
33640                 if(((!validanim(example[i], ANI_PICK) || example[i]->modeldata.animation[ANI_PICK]->loop.mode) && time > example[i]->stalltime) || !example[i]->animating)
33641                 {
33642                     ready[i] = 2;
33643                     exitdelay = time + GAME_SPEED;
33644                 }
33645             }
33646             else if(ready[i] == 2)
33647             {
33648                 font_printf(psmenu[i][2], psmenu[i][3], 0, 0, Tr("Ready!"));
33649             }
33650 
33651             if(example[i] != NULL)
33652             {
33653                 players_busy++;
33654             }
33655             if(ready[i] == 2)
33656             {
33657                 players_ready++;
33658             }
33659         }
33660 
33661         if(players_busy && players_busy == players_ready && exitdelay && time > exitdelay)
33662         {
33663             exit = 1;
33664         }
33665         update(0, 0);
33666 
33667         if(bothnewkeys & FLAG_ESC)
33668         {
33669             escape = 1;
33670         }
33671     }
33672 
33673     // No longer at the select screen
33674     kill_all();
33675     sound_close_music();
33676     selectScreen = 0;
33677 
33678     return (!escape);
33679 }
33680 
playgame(int * players,unsigned which_set,int useSavedGame)33681 void playgame(int *players,  unsigned which_set, int useSavedGame)
33682 {
33683     int i;
33684     current_level = 0;
33685     current_stage = 1;
33686     current_set = which_set;
33687     s_set_entry *set = levelsets + current_set;
33688     s_savelevel *save = savelevel + current_set;
33689     s_level_entry *le;
33690 
33691     useSave = 0;
33692     useSet = -1;
33693 
33694     if(which_set >= num_difficulties)
33695     {
33696         return;
33697     }
33698     // shutdown(1, "Illegal set chosen: index %i (there are only %i sets)!", which_set, num_difficulties);
33699 
33700     allow_secret_chars = set->ifcomplete;
33701     PLAYER_LIVES = set->lives;
33702     musicoverlap = set->musicoverlap;
33703     fade = set->custfade;
33704     CONTINUES = set->credits;
33705     magic_type = set->typemp;
33706     if(PLAYER_LIVES == 0)
33707     {
33708         PLAYER_LIVES = 3;
33709     }
33710     if(CONTINUES == 0)
33711     {
33712         CONTINUES = 5;
33713     }
33714     if(fade == 0)
33715     {
33716         fade = 24;
33717     }
33718     sameplayer = set->nosame;
33719 
33720     if(useSavedGame == 1 && save->flag)
33721     {
33722         memset(player, 0, sizeof(*player) * 4);
33723         if(!loadScriptFile())
33724         {
33725             printf("Warning, failed to load script save!\n");
33726         }
33727         current_level = save->level;
33728         current_stage = save->stage;
33729         if(save->flag == 2) // don't check 1 or 0 becuase if we use saved game the flag must be >0
33730         {
33731             for(i = 0; i < set->maxplayers; i++)
33732             {
33733                 player[i].lives = save->pLives[i];
33734                 player[i].credits = save->pCredits[i];
33735                 player[i].score = save->pScores[i];
33736                 player[i].colourmap = save->pColourmap[i];
33737                 player[i].weapnum = save->pWeapnum[i];
33738                 player[i].spawnhealth = save->pSpawnhealth[i];
33739                 player[i].spawnmp = save->pSpawnmp[i];
33740                 strncpy(player[i].name, save->pName[i], MAX_NAME_LEN);
33741             }
33742             credits = save->credits;
33743         }
33744         load_playable_list(save->allowSelectArgs); //TODO: change sav format to support dynamic allowselect list.
33745         //reset_playable_list(1); // add this because there's no select screen, temporary solution
33746     }
33747 
33748     nosave = 1;
33749 
33750     // fix save an inexistent level
33751     if ( current_level >= set->numlevels )
33752     {
33753         current_level = set->numlevels-1;
33754     }
33755 
33756     le = set->levelorder + current_level;
33757     set->noselect = le->noselect;
33758     if (save->selectSkipSelect[0]) le = load_skipselect(save->selectSkipSelect, set); // LOAD skipselect
33759     for(i = 0; i < MAX_PLAYERS; i++)
33760     {
33761         if(le->skipselect[i])
33762         {
33763             strcpy(skipselect[i], le->skipselect[i]);
33764         }
33765         else
33766         {
33767             skipselect[i][0] = 0;
33768         }
33769     }
33770 
33771     if((useSavedGame == 1 && save->flag == 2) || useSavedGame == 2 || selectplayer(players, NULL, useSavedGame)) // if save flag is 2 don't select player
33772     {
33773         while(current_level < set->numlevels)
33774         {
33775             if(branch_name[0])  // branch checking
33776             {
33777                 //current_stage = 1; //jump, jump... perhaps we don't need to reset it, modders should take care of it.
33778                 for(i = 0; i < set->numlevels; i++)
33779                 {
33780                     if(set->levelorder[i].branchname && stricmp(set->levelorder[i].branchname, branch_name) == 0)
33781                     {
33782                         current_level = i;
33783                         break;
33784                     }
33785                     //if(levelorder[which_set][i]->gonext==1) ++current_stage; OX. Commented this line out. Seems to be cause of inacurate stage # complete message.
33786                 }
33787                 branch_name[0] = 0;// clear up so we won't stuck here
33788             }
33789             le = set->levelorder + current_level;
33790             PLAYER_MIN_Z = le->z_coords[0];
33791             PLAYER_MAX_Z = le->z_coords[1];
33792             BGHEIGHT = le->z_coords[2];
33793 
33794             if(le->type == LE_TYPE_CUT_SCENE)
33795             {
33796                 playscene(le->filename);
33797             }
33798             else if(le->type == LE_TYPE_SELECT_SCREEN)
33799             {
33800                 memset(save->selectSkipSelect,0,sizeof(save->selectSkipSelect)); // RESET skipselect
33801                 set->noselect = 0;
33802                 for(i = 0; i < set->maxplayers; i++) // reset skipselect
33803                 {
33804                     if(le->skipselect[i])
33805                     {
33806                         skipselect[i][0] = 0;
33807                     }
33808                 }
33809                 for(i = 0; i < set->maxplayers ; i++)
33810                 {
33811                     players[i] = (player[i].lives > 0);
33812                 }
33813                 if(selectplayer(players, le->filename, useSavedGame) == 0)
33814                 {
33815                     break;
33816                 }
33817             }
33818             else if(le->type == LE_TYPE_SKIP_SELECT)
33819             {
33820                 set->noselect = le->noselect;
33821                 for(i = 0; i < MAX_PLAYERS; i++)
33822                 {
33823                     if(le->skipselect[i])
33824                     {
33825                         strcpy(skipselect[i], le->skipselect[i]);
33826                     }
33827                     else
33828                     {
33829                         skipselect[i][0] = 0;
33830                     }
33831                 }
33832                 save_skipselect(save->selectSkipSelect, skipselect); // TO SAVE FILE
33833                 selectplayer(players, NULL, useSavedGame); // re-select a player
33834             }
33835             else if(!playlevel(le->filename))
33836             {
33837                 if(player[0].lives <= 0 && player[1].lives <= 0 && player[2].lives <= 0 && player[3].lives <= 0)
33838                 {
33839                     if(!set->noshowgameover && !(goto_mainmenu_flag&2)) gameover();
33840                     if(!set->noshowhof && !(goto_mainmenu_flag&4))
33841                     {
33842                         hallfame(1);
33843                     }
33844                     for(i = 0; i < set->maxplayers; i++)
33845                     {
33846                         player[i].hasplayed = 0;
33847                         player[i].weapnum = 0;
33848                     }
33849                 }
33850                 break;
33851             }
33852             if(le->gonext == 1)
33853             {
33854                 if (!set->noshowcomplete) showcomplete(current_stage);
33855                 for(i = 0; i < set->maxplayers; i++)
33856                 {
33857                     player[i].spawnhealth = 0;
33858                     player[i].spawnmp = 0;
33859                 }
33860                 ++current_stage;
33861                 save->stage = current_stage;
33862             }
33863             current_level++;
33864             le = set->levelorder + current_level;
33865             save->level = current_level;
33866             //2007-2-24, gonext = 2, end game
33867             if((le - 1)->gonext == 2)
33868             {
33869                 current_level = set->numlevels;
33870             }
33871             if(useSave)
33872             {
33873                 goto endgame;    //quick exit without saving, for script load game logic
33874             }
33875         }//while
33876 
33877         if(current_level >= set->numlevels)
33878         {
33879             bonus += save->times_completed++;
33880             saveGameFile();
33881             if(!nofadeout)
33882             {
33883                 fade_out(0, 0);
33884             }
33885             if(!set->noshowhof)
33886             {
33887                 hallfame(1);
33888             }
33889         }
33890     }
33891 
33892 endgame:
33893     // clear global script variant list
33894     branch_name[0] = 0;
33895     sound_close_music();
33896 }
33897 
menu_difficulty()33898 int menu_difficulty()
33899 {
33900     int quit = 0;
33901     int selector = 0;
33902     int maxdisplay = 5;
33903     int i, j, t;
33904     //float slider = 0;
33905     int barx, bary, barw, barh;
33906     s_drawmethod drawmethod = plainmethod;
33907     drawmethod.alpha = 1;
33908 
33909     barx = videomodes.hRes / 5;
33910     bary = _liney(0, 0) - 2;
33911     barw = videomodes.hRes * 3 / 5;
33912     barh = 5 * (fontheight(0) + 1) + 4;
33913     newgameMenu = 1;
33914     bothnewkeys = 0;
33915 
33916     loadGameFile();
33917 
33918     while(!quit)
33919     {
33920         if(num_difficulties > 1)
33921         {
33922             _menutextm(2, -2, 0, Tr("Game Mode"));
33923             t = (selector - (selector == num_difficulties)) / maxdisplay * maxdisplay;
33924             for(j = 0, i = t; i < maxdisplay + t && i < num_difficulties; j++, i++)
33925             {
33926                 if(j < maxdisplay)
33927                 {
33928                     if(bonus >= levelsets[i].ifcomplete)
33929                     {
33930                         _menutextm((selector == i), j, 0, "%s", levelsets[i].name);
33931                     }
33932                     else
33933                     {
33934                         if(levelsets[i].ifcomplete > 1)
33935                         {
33936                             _menutextm((selector == i), j, 0, Tr("%s - Finish Game %i Times To UnLock"), levelsets[i].name, levelsets[i].ifcomplete);
33937                         }
33938                         else
33939                         {
33940                             _menutextm((selector == i), j, 0, Tr("%s - Finish Game To UnLock"), levelsets[i].name);
33941                         }
33942                     }
33943                 }
33944                 else
33945                 {
33946                     break;
33947                 }
33948             }
33949             _menutextm((selector == i), 6, 0, Tr("Back"));
33950 
33951             //draw the scroll bar
33952             if(num_difficulties > maxdisplay)
33953             {
33954                 spriteq_add_box(barx,  bary,        barw,     barh,   0, color_black, &drawmethod); //outerbox
33955                 spriteq_add_line(barx, bary,  barx + 8, bary, 1, color_white, NULL);
33956                 spriteq_add_line(barx, bary, barx, bary + barh, 1, color_white, NULL);
33957                 spriteq_add_line(barx + 8, bary, barx + 8, bary + barh,  1, color_white, NULL);
33958                 spriteq_add_line(barx, bary + barh, barx + 8, bary + barh,  1, color_white, NULL);
33959                 spriteq_add_box(barx + 1,  bary + selector * (barh - 3) / num_difficulties, 7,             3,            2, color_white, NULL); //slider
33960             }
33961         }
33962 
33963         update(0, 0);
33964 
33965         if(num_difficulties == 1) // OX. Mods with only one set will auto load that difficulty.
33966         {
33967             if(selector == num_difficulties)
33968             {
33969                 quit = 1;
33970             }
33971             else if(bonus >= levelsets[selector].ifcomplete)
33972             {
33973                 saveslot = selector;
33974                 strncpy(savelevel[saveslot].dName, levelsets[saveslot].name, MAX_NAME_LEN + 1);
33975                 newgameMenu = 0;
33976                 return saveslot;
33977             }
33978         }
33979 
33980         if(bothnewkeys & FLAG_ESC)
33981         {
33982             quit = 1;
33983         }
33984         if(bothnewkeys & FLAG_MOVEUP)
33985         {
33986             --selector;
33987             if(SAMPLE_BEEP >= 0)
33988             {
33989                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
33990             }
33991         }
33992         if(bothnewkeys & FLAG_MOVEDOWN)
33993         {
33994             ++selector;
33995             if(SAMPLE_BEEP >= 0)
33996             {
33997                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
33998             }
33999         }
34000         if(selector < 0)
34001         {
34002             selector = num_difficulties;
34003         }
34004         if(selector > num_difficulties)
34005         {
34006             selector = 0;
34007         }
34008         //if(selector<num_difficulties) slider = selector * 4.5;
34009 
34010         if(bothnewkeys & FLAG_ANYBUTTON)
34011         {
34012 
34013             if(SAMPLE_BEEP2 >= 0)
34014             {
34015                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
34016             }
34017 
34018             if(selector == num_difficulties)
34019             {
34020                 quit = 1;
34021             }
34022             else if(bonus >= levelsets[selector].ifcomplete)
34023             {
34024                 saveslot = selector;
34025                 strncpy(savelevel[saveslot].dName, levelsets[saveslot].name, MAX_NAME_LEN + 1);
34026                 newgameMenu = 0;
34027                 return saveslot;
34028             }
34029         }
34030     }
34031     bothnewkeys = 0;
34032     newgameMenu = 0;
34033     return -1;
34034 }
34035 
load_saved_game()34036 int load_saved_game()
34037 {
34038     int quit = 0;
34039     int selector = 0;
34040     int savedStatus = 0;
34041     char name[256] = {""};
34042     int col1 = -8, col2 = 6;
34043 
34044     loadgameMenu = 1;
34045     bothnewkeys = 0;
34046 
34047     if((savedStatus = loadGameFile()))
34048     {
34049         getPakName(name, 0);
34050     }
34051     for(saveslot = 0; saveslot < num_difficulties; saveslot++) if(savelevel[saveslot].flag && savelevel[saveslot].level)
34052         {
34053             break;
34054         }
34055 
34056     while(!quit)
34057     {
34058         if(saveslot >= num_difficulties) // not found
34059         {
34060             _menutextm(2, -4, 0, Tr("Load Game"));
34061             _menutext(0, col1, -2, Tr("Saved File:"));
34062             _menutext(0, col2, -2, Tr("Not Found!"));
34063             _menutextm(1, 6, 0, Tr("Back"));
34064 
34065             selector = 2;
34066         }
34067         else
34068         {
34069             _menutextm(2, -4, 0, Tr("Load Game"));
34070             _menutext(0, col1, -2, Tr("Saved File:"));
34071             if(savedStatus)
34072             {
34073                 _menutext(0, col2, -2, "%s", name);
34074             }
34075             else
34076             {
34077                 _menutext(0, col2, -2, Tr("Not Found!"));
34078             }
34079 
34080             if(savedStatus)
34081             {
34082                 _menutext((selector == 0), col1, -1, Tr("Mode:"));
34083                 _menutext((selector == 0), col2, -1, "%s", savelevel[saveslot].dName);
34084                 _menutext(0, col1, 0, Tr("Stage:"));
34085                 _menutext(0, col2, 0, "%d", savelevel[saveslot].stage);
34086                 _menutext(0, col1, 1, Tr("Level:"));
34087                 _menutext(0, col2, 1, "%d", savelevel[saveslot].level);
34088                 _menutext(0, col1, 2, Tr("Credits:"));
34089 
34090                 if(noshare){
34091                     _menutext(0, col2, 2, "%d/%d/%d/%d",
34092                                   savelevel[saveslot].pCredits[0],
34093                                   savelevel[saveslot].pCredits[1], savelevel[saveslot].pCredits[2],
34094                                   savelevel[saveslot].pCredits[3]);
34095                 } else {
34096                     _menutext(0, col2, 2, "%d", savelevel[saveslot].credits);
34097                 }
34098 
34099                 _menutext(0, col1, 3, Tr("Player Lives:"));
34100                 _menutext(0, col2, 3, "%d/%d/%d/%d",
34101                           savelevel[saveslot].pLives[0],
34102                           savelevel[saveslot].pLives[1], savelevel[saveslot].pLives[2],
34103                           savelevel[saveslot].pLives[3]);
34104             }
34105             _menutextm((selector == 1), 6, 0, Tr("Back"));
34106         }
34107         update(0, 0);
34108 
34109         if(bothnewkeys & FLAG_ESC)
34110         {
34111             quit = 1;
34112         }
34113         if(selector == 0 && (bothnewkeys & FLAG_MOVELEFT))
34114         {
34115             while(1)
34116             {
34117                 --saveslot;
34118                 if(saveslot < 0)
34119                 {
34120                     saveslot = num_difficulties - 1;
34121                 }
34122                 if(savelevel[saveslot].flag && savelevel[saveslot].level)
34123                 {
34124                     break;
34125                 }
34126             }
34127             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34128         }
34129         if(selector == 0 && (bothnewkeys & FLAG_MOVERIGHT))
34130         {
34131             while(1)
34132             {
34133                 ++saveslot;
34134                 if(saveslot > num_difficulties - 1)
34135                 {
34136                     saveslot = 0;
34137                 }
34138                 if(savelevel[saveslot].flag && savelevel[saveslot].level)
34139                 {
34140                     break;
34141                 }
34142             }
34143             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34144         }
34145         if(bothnewkeys & FLAG_MOVEUP)
34146         {
34147             --selector;
34148             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34149         }
34150         if(bothnewkeys & FLAG_MOVEDOWN)
34151         {
34152             ++selector;
34153             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34154         }
34155         if(savedStatus)
34156         {
34157             if(selector < 0)
34158             {
34159                 selector = 1;
34160             }
34161             if(selector > 1)
34162             {
34163                 selector = 0;
34164             }
34165         }
34166         else
34167         {
34168             selector = 1;
34169         }
34170 
34171         if((bothnewkeys & FLAG_ANYBUTTON))
34172         {
34173             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
34174             switch(selector)
34175             {
34176             case 0:
34177                 return saveslot;
34178                 break;
34179             case 1:
34180                 quit = 1;
34181                 break;
34182             }
34183         }
34184     }
34185     bothnewkeys = 0;
34186     loadgameMenu = 0;
34187     return -1;
34188 }
34189 
choose_mode(int * players)34190 int choose_mode(int *players)
34191 {
34192     int quit = 0;
34193     int relback = 0;
34194     int selector = 0;
34195     int status = 0;
34196 
34197     startgameMenu = 1;
34198     bothnewkeys = 0;
34199 
34200     while(!quit)
34201     {
34202         _menutextm(2, 1, 0, Tr("Choose Mode"));
34203         _menutextm((selector == 0), 3, 0, Tr("New Game"));
34204         _menutextm((selector == 1), 4, 0, Tr("Load Game"));
34205         _menutextm((selector == 2), 6, 0, Tr("Back"));
34206 
34207         update(0, 0);
34208 
34209         if(bothnewkeys & FLAG_ESC)
34210         {
34211             quit = 1;
34212         }
34213         if(bothnewkeys & FLAG_MOVEUP)
34214         {
34215             --selector;
34216             if(SAMPLE_BEEP >= 0)
34217             {
34218                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34219             }
34220         }
34221         if(bothnewkeys & FLAG_MOVEDOWN)
34222         {
34223             ++selector;
34224             if(SAMPLE_BEEP >= 0)
34225             {
34226                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34227             }
34228         }
34229         if(selector < 0)
34230         {
34231             selector = 2;
34232         }
34233         if(selector > 2)
34234         {
34235             selector = 0;
34236         }
34237 
34238         if(bothnewkeys & FLAG_ANYBUTTON)
34239         {
34240             if(SAMPLE_BEEP2 >= 0)
34241             {
34242                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
34243             }
34244             switch(selector)
34245             {
34246             case 0:
34247                 status = menu_difficulty();
34248                 if(status != -1)
34249                 {
34250                     playgame(players, status, 0);
34251                     relback = 1;
34252                     quit = 1;
34253                 }
34254                 break;
34255             case 1:
34256                 status = load_saved_game();
34257                 if(status != -1)
34258                 {
34259                     playgame(players, status, 1);
34260                     relback = 1;
34261                     quit = 1;
34262                 }
34263                 break;
34264             default:
34265                 quit = 1;
34266                 break;
34267             }
34268         }
34269     }
34270     bothnewkeys = 0;
34271     startgameMenu = 0;
34272     return relback;
34273 }
34274 
term_videomodes()34275 void term_videomodes()
34276 {
34277     videomodes.hRes = 0;
34278     videomodes.vRes = 0;
34279     video_set_mode(videomodes);
34280     if(custScenes != NULL)
34281     {
34282         free(custScenes);
34283     }
34284     custScenes = NULL;
34285     if(custBkgrds != NULL)
34286     {
34287         free(custBkgrds);
34288     }
34289     custBkgrds = NULL;
34290     if(custLevels != NULL)
34291     {
34292         free(custLevels);
34293     }
34294     custLevels = NULL;
34295     if(custModels != NULL)
34296     {
34297         free(custModels);
34298     }
34299     custModels = NULL;
34300 }
34301 
34302 // Load Video Mode from file
init_videomodes(int log)34303 void init_videomodes(int log)
34304 {
34305     char *filename = "data/video.txt";
34306     int bits = 8, tmp;
34307     ptrdiff_t pos, len;
34308     size_t size;
34309     char *buf = NULL;
34310     char *command = NULL;
34311     char *value = NULL, *value2;
34312     ArgList arglist;
34313     char argbuf[MAX_ARG_LEN + 1] = "";
34314 
34315     if(log)
34316     {
34317         printf("Initializing video............\n");
34318     }
34319 
34320     // 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.
34321 #define tryfile(X) if((tmp=openpackfile(X,packfile))!=-1) { closepackfile(tmp); filename=X; goto readfile; }
34322 #if WIN || LINUX
34323     tryfile("data/videopc.txt");
34324 #elif WII
34325     tryfile("data/videowii.txt");
34326     if(CONF_GetAspectRatio() == CONF_ASPECT_16_9)
34327     {
34328         tryfile("data/video169.txt")
34329     }
34330     else
34331     {
34332         tryfile("data/video43.txt");
34333     }
34334 #elif PSP
34335     tryfile("data/videopsp.txt");
34336     tryfile("data/video169.txt");
34337 #elif DC
34338     tryfile("data/videodc.txt");
34339     tryfile("data/video43.txt");
34340 #elif WIZ
34341     tryfile("data/videowiz.txt");
34342     tryfile("data/video169.txt");
34343 #elif GP2X
34344     tryfile("data/videogp2x.txt");
34345     tryfile("data/video43.txt");
34346 #elif OPENDINGUX
34347     tryfile("data/videoopendingux.txt");
34348     tryfile("data/video43.txt");
34349 #elif SYMBIAN
34350     tryfile("data/videosymbian.txt");
34351 #endif
34352 #undef tryfile
34353 
34354 readfile:
34355     // Read file
34356     if(buffer_pakfile(filename, &buf, &size) != 1)
34357     {
34358         videoMode = 0;
34359         printf("'%s' not found.\n", filename);
34360         goto VIDEOMODES;
34361     }
34362 
34363     printf("Reading video settings from '%s'.\n", filename);
34364 
34365     // Now interpret the contents of buf line by line
34366     pos = 0;
34367     while(pos < size)
34368     {
34369         ParseArgs(&arglist, buf + pos, argbuf);
34370         command = GET_ARG(0);
34371 
34372         if(command && command[0])
34373         {
34374             if(stricmp(command, "video") == 0)
34375             {
34376                 value = GET_ARG(1);
34377                 if((value2 = strchr(value, 'x')))
34378                 {
34379                     videomodes.hRes = atoi(value);
34380                     videomodes.vRes = atoi(value2 + 1);
34381                     videoMode = 255;
34382                 }
34383                 else
34384                 {
34385                     videoMode = GET_INT_ARG(1);
34386                 }
34387             }
34388             else if(stricmp(command, "scenes") == 0)
34389             {
34390                 len = strlen(GET_ARG(1));
34391                 custScenes = malloc(len + 1);
34392                 strcpy(custScenes, GET_ARG(1));
34393                 custScenes[len] = 0;
34394             }
34395             else if(stricmp(command, "backgrounds") == 0)
34396             {
34397                 len = strlen(GET_ARG(1));
34398                 custBkgrds = malloc(len + 1);
34399                 strcpy(custBkgrds, GET_ARG(1));
34400                 custBkgrds[len] = 0;
34401             }
34402             else if(stricmp(command, "levels") == 0)
34403             {
34404                 len = strlen(GET_ARG(1));
34405                 custLevels = malloc(len + 1);
34406                 strcpy(custLevels, GET_ARG(1));
34407                 custLevels[len] = 0;
34408             }
34409             else if(stricmp(command, "models") == 0)
34410             {
34411                 len = strlen(GET_ARG(1));
34412                 custModels = malloc(len + 1);
34413                 strcpy(custModels, GET_ARG(1));
34414                 custModels[len] = 0;
34415             }
34416             else if(stricmp(command, "colourdepth") == 0)
34417             {
34418                 pixelformat = PIXEL_x8;
34419                 value = GET_ARG(1);
34420                 if(stricmp(value, "8bit") == 0)
34421                 {
34422                     screenformat = PIXEL_8;
34423                     pixelformat = PIXEL_8;
34424                 }
34425                 else if(stricmp(value, "16bit") == 0)
34426                 {
34427                     screenformat = PIXEL_16;
34428                     bits = 16;
34429                 }
34430                 else if(stricmp(value, "32bit") == 0)
34431                 {
34432                     screenformat = PIXEL_32;
34433                     bits = 32;
34434                 }
34435                 else if(value[0] == 0)
34436                 {
34437                     screenformat = PIXEL_32;
34438                 }
34439                 else
34440                 {
34441                     shutdown(1, "Screen colour depth can only be either 8bit, 16bit or 32bit.");
34442                 }
34443             }
34444             else if(stricmp(command, "forcemode") == 0) {}
34445             else if(command && command[0])
34446             {
34447                 printf("Command '%s' not understood in file '%s'!", command, filename);
34448             }
34449         }
34450         // Go to next line
34451         pos += getNewLineStart(buf + pos);
34452     }
34453 
34454     if(buf != NULL)
34455     {
34456         free(buf);
34457         buf = NULL;
34458     }
34459 
34460 #if OPENDINGUX || GP2X
34461     videoMode = 0;
34462 #endif
34463 
34464 #if SYMBIAN
34465     if(videoMode != 0 && videoMode != 2)
34466     {
34467         videoMode = 0;
34468     }
34469 #endif
34470 
34471 VIDEOMODES:
34472     videomodes.mode    = videoMode;
34473     videomodes.filter  = savedata.swfilter;
34474     switch (videoMode)
34475     {
34476         // 320x240 - All Platforms
34477     case 0:
34478         videomodes.hRes    = 320;
34479         videomodes.vRes    = 240;
34480         videomodes.hScale  = 1;
34481         videomodes.vScale  = 1;
34482         videomodes.hShift  = 0;
34483         videomodes.vShift  = 0;
34484         videomodes.dOffset = 231;
34485         PLAYER_MIN_Z       = 160;
34486         PLAYER_MAX_Z       = 232;
34487         BGHEIGHT           = 160;
34488         break;
34489 
34490         // 480x272 - All Platforms
34491     case 1:
34492         videomodes.hRes    = 480;
34493         videomodes.vRes    = 272;
34494         videomodes.hScale  = (float)1.5;
34495         videomodes.vScale  = (float)1.13;
34496         videomodes.hShift  = 80;
34497         videomodes.vShift  = 20;
34498         videomodes.dOffset = 263;
34499         PLAYER_MIN_Z       = 182;
34500         PLAYER_MAX_Z       = 264;
34501         BGHEIGHT           = 182;
34502         break;
34503 
34504         // 640x480 - PC, Dreamcast, Wii
34505     case 2:
34506         videomodes.hRes    = 640;
34507         videomodes.vRes    = 480;
34508         videomodes.hScale  = 2;
34509         videomodes.vScale  = 2;
34510         videomodes.hShift  = 160;
34511         videomodes.vShift  = 35;
34512         videomodes.dOffset = 464;
34513         PLAYER_MIN_Z       = 321;
34514         PLAYER_MAX_Z       = 465;
34515         BGHEIGHT           = 321;
34516         break;
34517 
34518         // 720x480 - PC, Wii
34519     case 3:
34520         videomodes.hRes    = 720;
34521         videomodes.vRes    = 480;
34522         videomodes.hScale  = 2.25;
34523         videomodes.vScale  = 2;
34524         videomodes.hShift  = 200;
34525         videomodes.vShift  = 35;
34526         videomodes.dOffset = 464;
34527         PLAYER_MIN_Z       = 321;
34528         PLAYER_MAX_Z       = 465;
34529         BGHEIGHT           = 321;
34530         break;
34531 
34532         // 800x480 - PC, Wii, Pandora
34533     case 4:
34534         videomodes.hRes    = 800;
34535         videomodes.vRes    = 480;
34536         videomodes.hScale  = 2.5;
34537         videomodes.vScale  = 2;
34538         videomodes.hShift  = 240;
34539         videomodes.vShift  = 35;
34540         videomodes.dOffset = 464;
34541         PLAYER_MIN_Z       = 321;
34542         PLAYER_MAX_Z       = 465;
34543         BGHEIGHT           = 321;
34544         break;
34545 
34546         // 800x600 - PC, Dreamcast, Wii
34547     case 5:
34548         videomodes.hRes    = 800;
34549         videomodes.vRes    = 600;
34550         videomodes.hScale  = 2.5;
34551         videomodes.vScale  = 2.5;
34552         videomodes.hShift  = 240;
34553         videomodes.vShift  = 44;
34554         videomodes.dOffset = 580;
34555         PLAYER_MIN_Z       = 401;
34556         PLAYER_MAX_Z       = 582;
34557         BGHEIGHT           = 401;
34558         break;
34559 
34560         // 960x540 - PC, Wii
34561     case 6:
34562         videomodes.hRes    = 960;
34563         videomodes.vRes    = 540;
34564         videomodes.hScale  = 3;
34565         videomodes.vScale  = 2.25;
34566         videomodes.hShift  = 320;
34567         videomodes.vShift  = 40;
34568         videomodes.dOffset = 522;
34569         PLAYER_MIN_Z       = 362;
34570         PLAYER_MAX_Z       = 524;
34571         BGHEIGHT           = 362;
34572         break;
34573 
34574     case 255:
34575         videomodes.dOffset = videomodes.vRes * 0.9625;
34576         printf("\nUsing debug video mode: %d x %d\n", videomodes.hRes, videomodes.vRes);
34577         break;
34578     default:
34579         shutdown(1, "Invalid video mode: %d in 'data/video.txt', supported modes:\n"
34580                  "0 - 320x240\n"
34581                  "1 - 480x272\n"
34582                  "2 - 640x480\n"
34583                  "3 - 720x480\n"
34584                  "4 - 800x480\n"
34585                  "5 - 800x600\n"
34586                  "6 - 960x540\n\n", videoMode);
34587         break;
34588     }
34589 
34590 #if SDL || WII
34591     video_stretch(savedata.stretch);
34592 #endif
34593 
34594     if((vscreen = allocscreen(videomodes.hRes, videomodes.vRes, screenformat)) == NULL)
34595     {
34596         shutdown(1, "Not enough memory!\n");
34597     }
34598     videomodes.pixel = pixelbytes[(int)vscreen->pixelformat];
34599     //video_set_mode(videomodes);
34600     clearscreen(vscreen);
34601 
34602     if(log)
34603     {
34604         printf("Initialized video.............\t%dx%d (Mode: %d, Depth: %d Bit)\n\n", videomodes.hRes, videomodes.vRes, videoMode, bits);
34605     }
34606 }
34607 
34608 
34609 
34610 // ----------------------------------------------------------------------------
34611 
34612 
34613 // Set key or button safely (with switching)
safe_set(int * arr,int index,int newkey,int oldkey)34614 void safe_set(int *arr, int index, int newkey, int oldkey)
34615 {
34616     int i;
34617     for(i = 0; i < 12; i++)
34618     {
34619         if(arr[i] == newkey)
34620         {
34621             arr[i] = oldkey;
34622         }
34623     }
34624     arr[index] = newkey;
34625 }
34626 
34627 
keyboard_setup(int player)34628 void keyboard_setup(int player)
34629 {
34630     const int btnnum = MAX_BTN_NUM;
34631     int quit = 0, sdid,
34632         selector = 0,
34633         setting = -1,
34634         i, k, ok = 0,
34635               disabledkey[MAX_BTN_NUM+1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
34636                                 col1 = -8, col2 = 6;
34637     ptrdiff_t pos, voffset;
34638     size_t size;
34639     ArgList arglist;
34640     char argbuf[MAX_ARG_LEN + 1] = "";
34641     char *buf, *command, *filename = "data/menu.txt",
34642                           buttonnames[btnnum][16];
34643 
34644     printf("Loading control settings.......\t");
34645 
34646     strncpy(buttonnames[SDID_MOVEUP], "Move Up", 16);
34647     strncpy(buttonnames[SDID_MOVEDOWN], "Move Down", 16);
34648     strncpy(buttonnames[SDID_MOVELEFT], "Move Left", 16);
34649     strncpy(buttonnames[SDID_MOVERIGHT], "Move Right", 16);
34650     strncpy(buttonnames[SDID_ATTACK], "Attack 1", 16);
34651     strncpy(buttonnames[SDID_ATTACK2], "Attack 2", 16);
34652     strncpy(buttonnames[SDID_ATTACK3], "Attack 3", 16);
34653     strncpy(buttonnames[SDID_ATTACK4], "Attack 4", 16);
34654     strncpy(buttonnames[SDID_JUMP], "Jump", 16);
34655     strncpy(buttonnames[SDID_SPECIAL], "Special", 16);
34656     strncpy(buttonnames[SDID_START], "Start", 16);
34657     strncpy(buttonnames[SDID_SCREENSHOT], "Screenshot", 16);
34658     //strncpy(buttonnames[SDID_ESC], "Exit", 16);
34659 
34660     savesettings();
34661     bothnewkeys = 0;
34662 
34663     // Read file
34664     if(buffer_pakfile(filename, &buf, &size))
34665     {
34666         // Now interpret the contents of buf line by line
34667         pos = 0;
34668         while(pos < size)
34669         {
34670             ParseArgs(&arglist, buf + pos, argbuf);
34671             command = GET_ARG(0);
34672             if(command[0])
34673             {
34674                 if(stricmp(command, "disablekey") == 0)
34675                 {
34676                     sdid = translate_SDID(GET_ARG(1));
34677                     if(sdid >= 0)
34678                     {
34679                         disabledkey[sdid] = 1;
34680                     }
34681                 }
34682                 else if(stricmp(command, "renamekey") == 0)
34683                 {
34684                     sdid = translate_SDID(GET_ARG(1));
34685                     if(sdid >= 0)
34686                     {
34687                         strncpy(buttonnames[sdid], GET_ARG(2), 16);
34688                     }
34689                 }
34690 
34691             }
34692             // Go to next line
34693             pos += getNewLineStart(buf + pos);
34694         }
34695         if(buf != NULL)
34696         {
34697             free(buf);
34698             buf = NULL;
34699         }
34700     }
34701 
34702     while(disabledkey[selector]) if(++selector > btnnum-1)
34703         {
34704             break;
34705         }
34706 
34707     while(!quit)
34708     {
34709         voffset = -6;
34710         _menutextm(2, -8, 0, Tr("Player %i"), player + 1);
34711         for(i = 0; i < btnnum; i++)
34712         {
34713             if(!disabledkey[i])
34714             {
34715                 _menutext((selector == i), col1, voffset, "%s", buttonnames[i]);
34716                 _menutext((selector == i), col2, voffset, "%s", control_getkeyname(savedata.keys[player][i]));
34717                 voffset++;
34718             }
34719         }
34720         _menutextm((selector == btnnum), ++voffset, 0, Tr("OK"));
34721         _menutextm((selector == btnnum+1), ++voffset, 0, Tr("Cancel"));
34722         update((level != NULL), 0);
34723 
34724         if(setting > -1)
34725         {
34726             if(bothnewkeys & FLAG_ESC)
34727             {
34728                 savedata.keys[player][setting] = ok;
34729                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 50);
34730                 setting = -1;
34731             }
34732             if(setting > -1)
34733             {
34734                 k = control_scankey();
34735                 if(k)
34736                 {
34737                     safe_set(savedata.keys[player], setting, k, ok);
34738                     sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
34739                     setting = -1;
34740                     // Prevent accidental screenshot
34741                     bothnewkeys = 0;
34742                 }
34743             }
34744         }
34745         else
34746         {
34747             if(bothnewkeys & FLAG_ESC)
34748             {
34749                 quit = 1;
34750             }
34751             if(bothnewkeys & FLAG_MOVEUP)
34752             {
34753                 do
34754                 {
34755                     if(--selector < 0)
34756                     {
34757                         break;
34758                     }
34759                 }
34760                 while(disabledkey[selector]);
34761                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34762             }
34763             if(bothnewkeys & FLAG_MOVEDOWN)
34764             {
34765                 do
34766                 {
34767                     if(++selector > btnnum-1)
34768                     {
34769                         break;
34770                     }
34771                 }
34772                 while(disabledkey[selector]);
34773                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34774             }
34775             if(selector < 0)
34776             {
34777                 selector = btnnum+1;
34778             }
34779             if(selector > btnnum+1)
34780             {
34781                 selector = 0;
34782                 while(disabledkey[selector]) if(++selector > btnnum-1)
34783                     {
34784                         break;
34785                     }
34786             }
34787             if(bothnewkeys & FLAG_ANYBUTTON)
34788             {
34789                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
34790                 if(selector == btnnum)
34791                 {
34792                     quit = 2;
34793                 }
34794                 else if(selector == btnnum+1)
34795                 {
34796                     quit = 1;
34797                 }
34798                 else
34799                 {
34800                     setting = selector;
34801                     ok = savedata.keys[player][setting];
34802                     savedata.keys[player][setting] = 0;
34803 #ifndef DC
34804                     keyboard_getlastkey();
34805 #endif
34806                 }
34807             }
34808         }
34809     }
34810 
34811     if(quit == 2)
34812     {
34813         apply_controls();
34814         savesettings();
34815     }
34816     else
34817     {
34818         loadsettings();
34819     }
34820 
34821 
34822     update(0, 0);
34823     bothnewkeys = 0;
34824     printf("Done!\n");
34825 }
34826 
menu_options_input()34827 void menu_options_input()
34828 {
34829     int quit = 0;
34830     int selector = 1; // 0
34831     int x_pos = -6;
34832 
34833     controloptionsMenu = 1;
34834     bothnewkeys = 0;
34835 
34836     while(!quit)
34837     {
34838         _menutextm(2, x_pos-1, 0, Tr("Control Options"));
34839 
34840 #if PSP
34841         if(savedata.usejoy)
34842         {
34843             _menutext((selector == 0), -4, -2, Tr("Analog Pad Enabled"));
34844         }
34845         else
34846         {
34847             _menutext((selector == 0), -4, -2, Tr("Analog Pad Disabled"));
34848         }
34849 #elif WII
34850         if(savedata.usejoy)
34851         {
34852             _menutext((selector == 0), -4, -2, Tr("Nunchuk Analog Enabled"));
34853         }
34854         else
34855         {
34856             _menutext((selector == 0), -4, -2, Tr("Nunchuk Analog Disabled"));
34857         }
34858 #else
34859         if(savedata.usejoy)
34860         {
34861             _menutext((selector == 0),  x_pos, -2, Tr("GamePads Enabled"));
34862             if(!control_getjoyenabled())
34863             {
34864                 _menutext((selector == 0), x_pos+11, -2, Tr(" - Device Not Ready"));
34865             }
34866         }
34867         else
34868         {
34869             _menutext((selector == 0),  x_pos, -2, Tr("GamePads Disabled"));
34870         }
34871 #endif
34872 
34873         _menutext((selector == 1), x_pos,-1, Tr("Setup Player 1..."));
34874         _menutext((selector == 2), x_pos, 0, Tr("Setup Player 2..."));
34875         _menutext((selector == 3), x_pos, 1, Tr("Setup Player 3..."));
34876         _menutext((selector == 4), x_pos, 2, Tr("Setup Player 4..."));
34877         _menutextm((selector == 5), 5, 0, Tr("Back"));
34878         update((level != NULL), 0);
34879 
34880         if(bothnewkeys & FLAG_ESC)
34881         {
34882             quit = 1;
34883         }
34884         if(bothnewkeys & FLAG_MOVEUP)
34885         {
34886             --selector;
34887             if(SAMPLE_BEEP >= 0)
34888             {
34889                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34890             }
34891         }
34892         if(bothnewkeys & FLAG_MOVEDOWN)
34893         {
34894             ++selector;
34895             if(SAMPLE_BEEP >= 0)
34896             {
34897                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34898             }
34899         }
34900         if(selector < 0)
34901         {
34902             selector = 5;
34903         }
34904         if(selector > 5)
34905         {
34906             selector = 0;
34907         }
34908         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
34909         {
34910 
34911             if(SAMPLE_BEEP2 >= 0)
34912             {
34913                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
34914             }
34915 
34916             switch(selector)
34917             {
34918             case 0:
34919                 control_usejoy((savedata.usejoy ^= 1));
34920                 break;
34921             case 1:
34922                 keyboard_setup(0);
34923                 break;
34924             case 2:
34925                 keyboard_setup(1);
34926                 break;
34927             case 3:
34928                 keyboard_setup(2);
34929                 break;
34930             case 4:
34931                 keyboard_setup(3);
34932                 break;
34933             default:
34934                 quit = (bothnewkeys & FLAG_ANYBUTTON);
34935             }
34936         }
34937     }
34938     savesettings();
34939     bothnewkeys = 0;
34940     controloptionsMenu = 0;
34941 }
34942 
34943 
34944 
menu_options_sound()34945 void menu_options_sound()
34946 {
34947 
34948     int quit = 0;
34949     int selector = 0;
34950     int dir;
34951     int col1 = -8;
34952     int col2 = 6;
34953 
34954     soundoptionsMenu = 1;
34955     bothnewkeys = 0;
34956 
34957     while(!quit)
34958     {
34959         _menutextm(2, -5, 0, Tr("Sound Options"));
34960         _menutext((selector == 0), col1, -2, Tr("Sound Volume:"));
34961         _menutext((selector == 0), col2, -2, "%i", savedata.soundvol);
34962         _menutext((selector == 1), col1, -1, Tr("SFX Volume:"));
34963         _menutext((selector == 1), col2, -1, "%i", savedata.effectvol);
34964         _menutext((selector == 2), col1, 0, Tr("Music Volume:"));
34965         _menutext((selector == 2), col2, 0, "%i", savedata.musicvol);
34966         _menutext((selector == 3), col1, 1, Tr("BGM:"));
34967         _menutext((selector == 3), col2, 1, (savedata.usemusic ? Tr("Enabled") : Tr("Disabled")));
34968         _menutext((selector == 4), col1, 2, Tr("Show Titles:"));
34969         _menutext((selector == 4), col2, 2, (savedata.showtitles ? Tr("Yes") : Tr("No")));
34970         _menutext((selector == 5), col1, 3, Tr("Advanced Options..."));
34971         _menutextm((selector == 6), 6, 0, Tr("Back"));
34972 
34973         update((level != NULL), 0);
34974 
34975         if(bothnewkeys & FLAG_ESC)
34976         {
34977             quit = 1;
34978         }
34979         if(bothnewkeys & FLAG_MOVEUP)
34980         {
34981             --selector;
34982 
34983             if(SAMPLE_BEEP >= 0)
34984             {
34985                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34986             }
34987         }
34988         if(bothnewkeys & FLAG_MOVEDOWN)
34989         {
34990             ++selector;
34991 
34992             if(SAMPLE_BEEP >= 0)
34993             {
34994                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
34995             }
34996         }
34997         if(selector < 0)
34998         {
34999             selector = 6;
35000         }
35001         if(selector > 6)
35002         {
35003             selector = 0;
35004         }
35005 
35006         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
35007         {
35008             dir = 0;
35009 
35010             if(bothnewkeys & FLAG_MOVELEFT)
35011             {
35012                 dir = -1;
35013             }
35014             else if(bothnewkeys & FLAG_MOVERIGHT)
35015             {
35016                 dir = 1;
35017             }
35018 
35019             if(SAMPLE_BEEP2 >= 0)
35020             {
35021                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
35022             }
35023 
35024             switch(selector)
35025             {
35026             case 0:
35027                 savedata.soundvol += dir;
35028                 if(savedata.soundvol < 0)
35029                 {
35030                     savedata.soundvol = 0;
35031                 }
35032                 if(savedata.soundvol > 15)
35033                 {
35034                     savedata.soundvol = 15;
35035                 }
35036                 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
35037                 break;
35038             case 1:
35039                 savedata.effectvol += 4 * dir;
35040                 if(savedata.effectvol < 0)
35041                 {
35042                     savedata.effectvol = 0;
35043                 }
35044                 if(savedata.effectvol > 512)
35045                 {
35046                     savedata.effectvol = 512;
35047                 }
35048                 break;
35049             case 3:
35050                 if(!dir)
35051                 {
35052                     break;
35053                 }
35054                 if(!savedata.usemusic)
35055                 {
35056                     savedata.usemusic = 1;
35057                     music("data/music/remix", 1, 0);
35058                 }
35059                 else
35060                 {
35061                     savedata.usemusic = 0;
35062                     sound_close_music();
35063                 }
35064                 break;
35065             case 2:
35066                 savedata.musicvol += 4 * dir;
35067                 if(savedata.musicvol < 0)
35068                 {
35069                     savedata.musicvol = 0;
35070                 }
35071                 if(savedata.musicvol > 512)
35072                 {
35073                     savedata.musicvol = 512;
35074                 }
35075                 sound_volume_music(savedata.musicvol, savedata.musicvol);
35076                 break;
35077             case 4:
35078                 savedata.showtitles = !savedata.showtitles;
35079                 break;
35080             case 5:
35081                 menu_options_soundcard();
35082                 break;
35083             default:
35084                 quit = 1;
35085             }
35086         }
35087     }
35088     savesettings();
35089     bothnewkeys = 0;
35090     soundoptionsMenu = 0;
35091 }
35092 
menu_options_config()35093 void menu_options_config()     //  OX. Load from / save to default.cfg. Restore OpenBoR "factory" settings.
35094 {
35095     int quit = 0;
35096     int selector = 0;
35097     int saved = 0;
35098     int loaded = 0;
35099     int restored = 0;
35100 
35101     bothnewkeys = 0;
35102 
35103     while(!quit)
35104     {
35105         _menutextm(2, -5, 0, Tr("Configuration Settings"));
35106 
35107         if(saved == 1)
35108         {
35109             _menutextm((selector == 0), -3, 0, Tr("Save Settings To Default.cfg%s"), Tr("  Done!"));
35110         }
35111         else
35112         {
35113             _menutextm((selector == 0), -3, 0, Tr("Save Settings To Default.cfg%s"), "");
35114         }
35115 
35116         if(loaded == 1)
35117         {
35118             _menutextm((selector == 1), -2, 0, Tr("Load Settings From Default.cfg%s"), Tr("  Done!"));
35119         }
35120         else
35121         {
35122             _menutextm((selector == 1), -2, 0, Tr("Load Settings From Default.cfg%s"), "");
35123         }
35124 
35125         if(restored == 1)
35126         {
35127             _menutextm((selector == 2), -1, 0, Tr("Restore OpenBoR Defaults%s"), Tr("  Done!"));
35128         }
35129         else
35130         {
35131             _menutextm((selector == 2), -1, 0, Tr("Restore OpenBoR Defaults%s"), "");
35132         }
35133 
35134         _menutextm((selector == 3), 6, 0, Tr("Back"));
35135 
35136         update((level != NULL), 0);
35137 
35138         if(bothnewkeys & FLAG_ESC)
35139         {
35140             quit = 1;
35141         }
35142         if(bothnewkeys & FLAG_MOVEUP)
35143         {
35144             --selector;
35145 
35146             if(SAMPLE_BEEP >= 0)
35147             {
35148                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35149             }
35150         }
35151         if(bothnewkeys & FLAG_MOVEDOWN)
35152         {
35153             ++selector;
35154 
35155             if(SAMPLE_BEEP >= 0)
35156             {
35157                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35158             }
35159         }
35160 
35161         if(selector < 0)
35162         {
35163             selector = 3;
35164         }
35165         if(selector > 3)
35166         {
35167             selector = 0;
35168         }
35169 
35170         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
35171         {
35172 
35173             if(SAMPLE_BEEP2 >= 0)
35174             {
35175                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
35176             }
35177 
35178             switch(selector)
35179             {
35180             case 0:
35181                 saveasdefault();
35182                 saved = 1;
35183                 break;
35184 
35185             case 1:
35186                 loadfromdefault();
35187                 //shutdown(2, "\nSettings Loaded From Default.cfg. Restart Required.\n\n");
35188                 init_videomodes(0);
35189                 if(!video_set_mode(videomodes))
35190                 {
35191                     shutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
35192                 }
35193                 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
35194                 sound_volume_music(savedata.musicvol, savedata.musicvol);
35195                 loaded = 1;
35196                 break;
35197             case 2:
35198                 clearsettings();
35199                 //shutdown(2, "\nSettings Loaded From Default.cfg. Restart Required.\n\n");
35200                 init_videomodes(0);
35201                 if(!video_set_mode(videomodes))
35202                 {
35203                     shutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
35204                 }
35205                 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
35206                 sound_volume_music(savedata.musicvol, savedata.musicvol);
35207                 restored = 1;
35208                 break;
35209             default:
35210                 quit = 1;
35211             }
35212         }
35213     }
35214     savesettings();
35215     bothnewkeys = 0;
35216 }
35217 
menu_options_debug()35218 void menu_options_debug()
35219 {
35220     #define MENU_POS_Y              -4
35221     #define MENU_ITEMS_MARGIN_Y     2
35222     #define COLUMN_1_POS_X          -11
35223     #define COLUMN_2_POS_X          COLUMN_1_POS_X + 14
35224     #define MENU_ITEM_FIRST_INDEX   0
35225 
35226     // Selections enumerator. All
35227     // selection items should be placed
35228     // here first.
35229     typedef enum
35230     {
35231         // First item can be
35232         // whatever we like,
35233         // but it must be set
35234         // to the MENU_ITEM_FIRST_INDEX
35235         // constant.
35236         ITEM_PERFORMANCE = MENU_ITEM_FIRST_INDEX,
35237 
35238         // Items between the first
35239         // and last can go in any
35240         // order.
35241         ITEM_POSITION,
35242         ITEM_FEATURES,
35243         ITEM_COL_ATTACK,
35244         ITEM_COL_BODY,
35245         ITEM_COL_RANGE,
35246 
35247         // This is the "Back"
35248         // selection and should
35249         // always be last.
35250         ITEM_EXIT
35251     } e_selections;
35252 
35253     int pos_y;
35254     int quit                = 0;
35255     e_selections selector   = 0;
35256     bothnewkeys             = 0;
35257 
35258     while(!quit)
35259     {
35260         // Display menu title.
35261         _menutextm(2, MENU_POS_Y, 0, Tr("Debug Settings"));
35262 
35263         // Menu items.
35264         // Y position is controlled by a incremented integer.
35265         // Integer has a base value of the Menu title Y
35266         // plus static offset. Below each item, this Y value
35267         // increments by one. We can then add or remove
35268         // menu items here and the on screen position will
35269         // take care of itself without us needing to mess
35270         // with a bunch of hard constants.
35271 
35272         // Reset menu item position Y.
35273         pos_y = MENU_POS_Y + MENU_ITEMS_MARGIN_Y;
35274 
35275         _menutext((selector == ITEM_PERFORMANCE),    COLUMN_1_POS_X, pos_y, Tr("Performance:"));
35276         _menutext((selector == ITEM_PERFORMANCE),    COLUMN_2_POS_X, pos_y, (savedata.debuginfo ? Tr("Enabled") : Tr("Disabled")));
35277         pos_y++;
35278 
35279         _menutext((selector == ITEM_POSITION),       COLUMN_1_POS_X, pos_y, Tr("Position:"));
35280         _menutext((selector == ITEM_POSITION),       COLUMN_2_POS_X, pos_y, (savedata.debug_position ? Tr("Enabled") : Tr("Disabled")));
35281         pos_y++;
35282 
35283         _menutext((selector == ITEM_FEATURES),       COLUMN_1_POS_X, pos_y, Tr("Features:"));
35284         _menutext((selector == ITEM_FEATURES),       COLUMN_2_POS_X, pos_y, (savedata.debug_features ? Tr("Enabled") : Tr("Disabled")));
35285         pos_y++;
35286 
35287         _menutext((selector == ITEM_COL_ATTACK),     COLUMN_1_POS_X, pos_y, Tr("Collision Attack:"));
35288         _menutext((selector == ITEM_COL_ATTACK),     COLUMN_2_POS_X, pos_y, (savedata.debug_collision_attack ? Tr("Enabled") : Tr("Disabled")));
35289         pos_y++;
35290 
35291         _menutext((selector == ITEM_COL_BODY),       COLUMN_1_POS_X, pos_y, Tr("Collision Body:"));
35292         _menutext((selector == ITEM_COL_BODY),       COLUMN_2_POS_X, pos_y, (savedata.debug_collision_body ? Tr("Enabled") : Tr("Disabled")));
35293         pos_y++;
35294 
35295         _menutext((selector == ITEM_COL_RANGE),      COLUMN_1_POS_X, pos_y, Tr("Range:"));
35296         _menutext((selector == ITEM_COL_RANGE),      COLUMN_2_POS_X, pos_y, (savedata.debug_collision_range ? Tr("Enabled") : Tr("Disabled")));
35297         pos_y++;
35298 
35299         // Display exit title
35300         _menutextm((selector == ITEM_EXIT), pos_y + (MENU_ITEMS_MARGIN_Y), 0, Tr("Back"));
35301 
35302         // Run an engine update.
35303         update((level != NULL), 0);
35304 
35305         // If user presses up/down or esc, let's act accordingly.
35306         if(bothnewkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN | FLAG_ESC))
35307         {
35308             // Play beep if available.
35309             if(SAMPLE_BEEP >= 0)
35310             {
35311                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35312             }
35313 
35314             // If user presses escape, then set quit
35315             // flag immediately. Else wise, increment
35316             // or decrement selector as needed.
35317             if(bothnewkeys & FLAG_ESC)
35318             {
35319                 quit = 1;
35320             }
35321             else if(bothnewkeys & FLAG_MOVEUP)
35322             {
35323                 // If we are at the top item, loop
35324                 // to last. Otherwise, move one up.
35325                 if(selector <= MENU_ITEM_FIRST_INDEX)
35326                 {
35327                     selector = ITEM_EXIT;
35328                 }
35329                 else
35330                 {
35331                     --selector;
35332                 }
35333             }
35334             else if(bothnewkeys & FLAG_MOVEDOWN)
35335             {
35336                 // If we are at the last item
35337                 // (which should be "back"), then
35338                 // loop back to first. Otherwise
35339                 // move one down.
35340                 if(selector >= ITEM_EXIT)
35341                 {
35342                     selector = MENU_ITEM_FIRST_INDEX;
35343                 }
35344                 else
35345                 {
35346                     ++selector;
35347                 }
35348             }
35349         }
35350 
35351 
35352         // Toggle selection value on left/right or
35353         // trigger button press.
35354         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
35355         {
35356             if(SAMPLE_BEEP2 >= 0)
35357             {
35358                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
35359             }
35360 
35361             // This is where menu items are executed.
35362             switch(selector)
35363             {
35364                 case ITEM_PERFORMANCE:
35365                     savedata.debuginfo = !savedata.debuginfo;
35366                     break;
35367                 case ITEM_POSITION:
35368                     savedata.debug_position = !savedata.debug_position;
35369                     break;
35370                 case ITEM_FEATURES:
35371                     savedata.debug_features = !savedata.debug_features;
35372                     break;
35373                 case ITEM_COL_ATTACK:
35374                     savedata.debug_collision_attack = !savedata.debug_collision_attack;
35375                     break;
35376                 case ITEM_COL_BODY:
35377                     savedata.debug_collision_body = !savedata.debug_collision_body;
35378                     break;
35379                 case ITEM_COL_RANGE:
35380                     savedata.debug_collision_range = !savedata.debug_collision_range;
35381                     break;
35382                 case ITEM_EXIT:
35383                     quit = 1;
35384             }
35385         }
35386     }
35387     savesettings();
35388     bothnewkeys = 0;
35389 
35390     #undef MENU_POS_Y
35391     #undef MENU_ITEMS_MARGIN_Y
35392     #undef COLUMN_1_POS_X
35393     #undef COLUMN_2_POS_X
35394     #undef MENU_ITEM_FIRST_INDEX
35395 }
35396 
35397 
menu_options_system()35398 void menu_options_system()
35399 {
35400     #define SYS_OPT_Y_POS -4
35401 
35402     enum {
35403         SYS_OPT_LOG,
35404         SYS_OPT_VSDAMAGE,
35405         SYS_OPT_CHEATS,
35406         SYS_OPT_DEBUG,
35407         SYS_OPT_CONFIG,
35408         SYS_OPT_PSP_CPUSPEED,
35409         SYS_OPT_BACK
35410     };
35411 
35412     int quit = 0;
35413     int selector = 0;
35414     int col1 = -11;
35415     int col2 = col1+14;
35416     int ex_labels = 0;
35417     int RET = SYS_OPT_BACK-1;
35418 
35419 #if PSP
35420     int dir = 0;
35421     int batteryPercentage = 0;
35422     int batteryLifeTime = 0;
35423     int externalPower = 0;
35424 #endif
35425 
35426     systemoptionsMenu = 1;
35427     bothnewkeys = 0;
35428     if (nodebugoptions) ex_labels = 1;
35429     RET -= ex_labels;
35430 
35431     while(!quit)
35432     {
35433         _menutextm(2, SYS_OPT_Y_POS-2, 0, Tr("System Options"));
35434 
35435         _menutext(0, col1, SYS_OPT_Y_POS, Tr("Total RAM:"));
35436         _menutext(0, col2, SYS_OPT_Y_POS, Tr("%s KB"), commaprint(getSystemRam(KBYTES)));
35437 
35438         _menutext(0, col1, SYS_OPT_Y_POS+1, Tr("Used RAM:"));
35439         _menutext(0, col2, SYS_OPT_Y_POS+1, Tr("%s KB"), commaprint(getUsedRam(KBYTES)));
35440 
35441         _menutext(0, col1, SYS_OPT_Y_POS+2, Tr("Max Players:"));
35442         _menutext(0, col2, SYS_OPT_Y_POS+2, Tr("%i"), levelsets[current_set].maxplayers);
35443 
35444         _menutext((selector == SYS_OPT_LOG), col1, SYS_OPT_Y_POS+4, Tr("File Logging:"));
35445         _menutext((selector == SYS_OPT_LOG), col2, SYS_OPT_Y_POS+4, (savedata.uselog ? Tr("Enabled") : Tr("Disabled")));
35446 
35447         _menutext((selector == SYS_OPT_VSDAMAGE), col1, SYS_OPT_Y_POS+5, Tr("Versus Damage:"), 0);
35448         if(versusdamage == 0)
35449         {
35450             _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Disabled by Module"));
35451         }
35452         else if(versusdamage == 1)
35453         {
35454             _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Enabled by Module"));
35455         }
35456         else
35457         {
35458             if(savedata.mode)
35459             {
35460                 _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Disabled"));    //Mode 1 - Players CAN'T attack each other
35461             }
35462             else
35463             {
35464                 _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS+5, Tr("Enabled"));    //Mode 2 - Players CAN attack each other
35465             }
35466         }
35467 
35468         _menutext((selector == SYS_OPT_CHEATS), col1, SYS_OPT_Y_POS+6, Tr("Cheats:"));
35469         _menutext((selector == SYS_OPT_CHEATS), col2, SYS_OPT_Y_POS+6, forcecheatsoff ? Tr("Disabled by Module") : (cheats ? Tr("On") : Tr("Off")));
35470         if(!nodebugoptions) _menutext((selector == SYS_OPT_DEBUG), col1, SYS_OPT_Y_POS+7, Tr("Debug Settings..."));
35471 
35472 #ifndef DC
35473 
35474         _menutext((selector == SYS_OPT_CONFIG-ex_labels), col1, SYS_OPT_Y_POS+8-ex_labels, Tr("Config Settings..."));
35475 
35476 #endif
35477 
35478 #if PSP
35479         externalPower = scePowerIsPowerOnline();
35480         _menutext((selector == SYS_OPT_PSP_CPUSPEED), col1, 4-ex_labels, Tr("CPU Speed:"));
35481         _menutext((selector == SYS_OPT_PSP_CPUSPEED), col2, 4-ex_labels, "%d MHz", scePowerGetCpuClockFrequency());
35482         if(!externalPower)
35483         {
35484             batteryPercentage = scePowerGetBatteryLifePercent();
35485             batteryLifeTime = scePowerGetBatteryLifeTime();
35486             _menutext(0, col1, 5-ex_labels, Tr("Battery:"));
35487             if(batteryPercentage < 0 || batteryLifeTime < 0)
35488             {
35489                 _menutext(0, col2, 5-ex_labels, Tr("Calculating..."));
35490             }
35491             else
35492             {
35493                 _menutext(0, col2, 5-ex_labels, "%d%% - %02d:%02d", batteryPercentage, batteryLifeTime / 60, batteryLifeTime - (batteryLifeTime / 60 * 60));
35494             }
35495         }
35496         else
35497         {
35498             _menutext(0, col1, 5-ex_labels, Tr("Charging:"));
35499             _menutext(0, col2, 5-ex_labels, Tr("%d%% AC Power"), scePowerGetBatteryLifePercent());
35500         }
35501         RET = 6-ex_labels;
35502 #endif
35503 
35504         _menutextm((selector == RET), SYS_OPT_Y_POS+11-ex_labels, 0, Tr("Back"));
35505 
35506         update((level != NULL), 0);
35507 
35508         if(bothnewkeys & FLAG_ESC)
35509         {
35510             quit = 1;
35511         }
35512         if(bothnewkeys & FLAG_MOVEUP)
35513         {
35514             --selector;
35515             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35516         }
35517         if(bothnewkeys & FLAG_MOVEDOWN)
35518         {
35519             ++selector;
35520             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35521         }
35522 
35523         if(selector < 0)
35524         {
35525             selector = RET;
35526         }
35527         if(selector > RET)
35528         {
35529             selector = 0;
35530         }
35531 
35532         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
35533         {
35534 
35535 #if PSP
35536             dir = 0;
35537             if(bothnewkeys & FLAG_MOVELEFT)
35538             {
35539                 dir = -1;
35540             }
35541             else if(bothnewkeys & FLAG_MOVERIGHT)
35542             {
35543                 dir = 1;
35544             }
35545 #endif
35546 
35547             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
35548 
35549                  if (selector==RET) quit = 1;
35550             else if (selector==SYS_OPT_LOG) savedata.uselog =  !savedata.uselog;
35551             else if (selector==SYS_OPT_VSDAMAGE)
35552             {
35553                 if(versusdamage > 1)
35554                 {
35555                     if(savedata.mode)
35556                     {
35557                         savedata.mode = 0;
35558                     }
35559                     else
35560                     {
35561                         savedata.mode = 1;
35562                     }
35563                 }
35564             }
35565             else if (selector==SYS_OPT_CHEATS) cheats = !cheats;
35566             else if (selector==SYS_OPT_DEBUG && !nodebugoptions) menu_options_debug();
35567 #ifndef DC
35568             else if (selector==SYS_OPT_CONFIG-ex_labels) menu_options_config();
35569 #endif
35570 
35571 #ifdef PSP
35572             else if (selector==SYS_OPT_PSP_CPUSPEED-ex_labels)
35573             {
35574                 savedata.pspcpuspeed += dir;
35575                 if(savedata.pspcpuspeed < 0)
35576                 {
35577                     savedata.pspcpuspeed = 2;
35578                 }
35579                 if(savedata.pspcpuspeed > 2)
35580                 {
35581                     savedata.pspcpuspeed = 0;
35582                 }
35583 
35584                 switch(savedata.pspcpuspeed)
35585                 {
35586                 case 0:
35587                     scePowerSetClockFrequency(222, 222, 111);
35588                     break;
35589                 case 1:
35590                     scePowerSetClockFrequency(266, 266, 133);
35591                     break;
35592                 case 2:
35593                     scePowerSetClockFrequency(333, 333, 166);
35594                     break;
35595                 }
35596             }
35597 #endif
35598             else quit = 1;
35599         }
35600     }
35601     savesettings();
35602     bothnewkeys = 0;
35603     systemoptionsMenu = 0;
35604 
35605     #undef SYS_OPT_Y_POS
35606 }
35607 
35608 
menu_options_video()35609 void menu_options_video()
35610 {
35611     int quit = 0;
35612     int selector = 0;
35613     int dir;
35614     int col1 = -15, col2 = 1;
35615 
35616     videooptionsMenu = 1;
35617     bothnewkeys = 0;
35618 
35619     while(!quit)
35620     {
35621         _menutextm(2, -5, 0, Tr("Video Options"));
35622         _menutext((selector == 0), col1, -3, Tr("Brightness:"));
35623         _menutext((selector == 0), col2, -3, "%i", savedata.brightness);
35624         _menutext((selector == 1), col1, -2, Tr("Gamma:"));
35625         _menutext((selector == 1), col2, -2, "%i", savedata.gamma);
35626         _menutext((selector == 2), col1, -1, Tr("Window Offset:"));
35627         _menutext((selector == 2), col2, -1, "%i", savedata.windowpos);
35628 
35629 #if OPENDINGUX
35630         _menutext((selector == 3), col1, 0, Tr("Display Mode:"));
35631         _menutext((selector == 3), col2, 0, savedata.fullscreen ? Tr("Full") : Tr("Window"));
35632         _menutextm((selector == 4), 6, 0, Tr("Back"));
35633         if(selector < 0)
35634         {
35635             selector = 4;
35636         }
35637         if(selector > 4)
35638         {
35639             selector = 0;
35640         }
35641 #endif
35642 
35643 #if DC || GP2X
35644         _menutextm((selector == 3), 6, 0, Tr("Back"));
35645         if(selector < 0)
35646         {
35647             selector = 3;
35648         }
35649         if(selector > 3)
35650         {
35651             selector = 0;
35652         }
35653 #endif
35654 
35655 #if WII
35656         _menutext((selector == 3), col1, 0, Tr("Display Mode:"));
35657         _menutext((selector == 3), col2, 0, (savedata.stretch ? Tr("Stretch to Screen") : Tr("Preserve Aspect Ratio")));
35658         _menutextm((selector == 4), 6, 0, Tr("Back"));
35659         if(selector < 0)
35660         {
35661             selector = 4;
35662         }
35663         if(selector > 4)
35664         {
35665             selector = 0;
35666         }
35667 #endif
35668 
35669 #if SDL
35670 #if !defined(GP2X) && !defined(OPENDINGUX)
35671         _menutext((selector == 3), col1, 0, Tr("Display Mode:"));
35672         _menutext((selector == 3), col2, 0, savedata.fullscreen ? Tr("Full") : Tr("Window"));
35673 
35674         _menutext((selector == 4), col1, 1, Tr("Video Backend:"));
35675         _menutext((selector == 4), col2, 1, (opengl ? Tr("OpenGL") : Tr("SDL")));
35676 
35677         _menutext((selector == 5), col1, 2, Tr("Scale:"));
35678 #ifdef ANDROID
35679         if(savedata.glscale == 0)
35680 #else
35681 		if(savedata.fullscreen)
35682 #endif
35683         {
35684             _menutext((selector == 5), col2, 2, Tr("Automatic"));
35685         }
35686         else
35687         {
35688             _menutext((selector == 5), col2, 2, "%4.2fx - %ix%i", savedata.glscale, (int)(videomodes.hRes * savedata.glscale), (int)(videomodes.vRes * savedata.glscale));
35689         }
35690 
35691         _menutext((selector == 6), col1, 3, Tr("Hardware Filter:"));
35692         _menutext((selector == 6), col2, 3, ((savedata.glscale != 1.0 || savedata.fullscreen) ? (savedata.glfilter[savedata.fullscreen] ? Tr("Simple") : Tr("Bilinear")) : Tr("Disabled")));
35693 
35694         _menutext((selector == 7), col1, 4, Tr("Software Filter:"));
35695         _menutext((selector == 7), col2, 4, ((savedata.glscale >= 2.0 || savedata.fullscreen) ? Tr(GfxBlitterNames[savedata.swfilter]) : Tr("Disabled")));
35696 
35697         if(savedata.fullscreen)
35698         {
35699             _menutext((selector == 8), col1, 5, Tr("Fullscreen Type:"));
35700             _menutext((selector == 8), col2, 5, (savedata.stretch ? Tr("Stretch to Screen") : Tr("Preserve Aspect Ratio")));
35701         }
35702         else if(selector == 8)
35703         {
35704             selector = (bothnewkeys & FLAG_MOVEUP) ? 7 : 9;
35705         }
35706 
35707         _menutextm((selector == 9), 7, 0, Tr("Back"));
35708         if(selector < 0)
35709         {
35710             selector = 9;
35711         }
35712         if(selector > 9)
35713         {
35714             selector = 0;
35715         }
35716 #endif
35717 #endif
35718 
35719 #if PSP
35720         _menutext((selector == 3), col1, 0, Tr("Screen:"));
35721         _menutext((selector == 3), col2, 0, displayFormat[(int)videomodes.mode].name);
35722         _menutext((selector == 4), col1, 1, Tr("Filters:"));
35723         _menutext((selector == 4), col2, 1, filterName[(int)videomodes.filter]);
35724         _menutext((selector == 5), col1, 2, Tr("Display:"));
35725         _menutext((selector == 5), col2, 2, displayName[displayMode]);
35726         _menutext((selector >= 6 && selector <= 9), col1, 3, Tr("Overscan:"));
35727         _menutext((selector >= 6 && selector <= 9), col2 + 1.5, 3, ".");
35728         _menutext((selector >= 6 && selector <= 9), col2 + 3.5, 3, ".");
35729         _menutext((selector >= 6 && selector <= 9), col2 + 5.5, 3, ".");
35730         _menutext((selector == 6), col2, 3, "%02d", savedata.overscan[0]);
35731         _menutext((selector == 7), col2 + 2, 3, "%02d", savedata.overscan[1]);
35732         _menutext((selector == 8), col2 + 4, 3, "%02d", savedata.overscan[2]);
35733         _menutext((selector == 9), col2 + 6, 3, "%02d", savedata.overscan[3]);
35734         _menutextm((selector == 10), 6, 0, Tr("Back"));
35735         if(selector < 0)
35736         {
35737             selector = 10;
35738         }
35739         if(selector > 10)
35740         {
35741             selector = 0;
35742         }
35743 #endif
35744 
35745         update((level != NULL), 0);
35746 
35747         if(bothnewkeys & FLAG_ESC)
35748         {
35749             quit = 1;
35750         }
35751         if(bothnewkeys & FLAG_MOVEUP)
35752         {
35753             --selector;
35754             if(SAMPLE_BEEP >= 0)
35755             {
35756                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35757             }
35758         }
35759         if(bothnewkeys & FLAG_MOVEDOWN)
35760         {
35761             ++selector;
35762             if(SAMPLE_BEEP >= 0)
35763             {
35764                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
35765             }
35766         }
35767         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
35768         {
35769             dir = 0;
35770 
35771             if(bothnewkeys & FLAG_MOVELEFT)
35772             {
35773                 dir = -1;
35774             }
35775             else if(bothnewkeys & FLAG_MOVERIGHT)
35776             {
35777                 dir = 1;
35778             }
35779 
35780             if(SAMPLE_BEEP2 >= 0)
35781             {
35782                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
35783             }
35784 
35785             switch(selector)
35786             {
35787             case 0:
35788                 savedata.brightness += 8 * dir;
35789                 if(savedata.brightness < -256)
35790                 {
35791                     savedata.brightness = -256;
35792                 }
35793                 if(savedata.brightness > 256)
35794                 {
35795                     savedata.brightness = 256;
35796                 }
35797                 vga_vwait();
35798                 set_color_correction(savedata.gamma, savedata.brightness);
35799                 break;
35800             case 1:
35801                 savedata.gamma += 8 * dir;
35802                 if(savedata.gamma < -256)
35803                 {
35804                     savedata.gamma = -256;
35805                 }
35806                 if(savedata.gamma > 256)
35807                 {
35808                     savedata.gamma = 256;
35809                 }
35810                 vga_vwait();
35811                 set_color_correction(savedata.gamma, savedata.brightness);
35812                 break;
35813             case 2:
35814                 savedata.windowpos += dir;
35815                 if(savedata.windowpos < -2)
35816                 {
35817                     savedata.windowpos = -2;
35818                 }
35819                 if(savedata.windowpos > 20)
35820                 {
35821                     savedata.windowpos = 20;
35822                 }
35823                 break;
35824 #if SDL || PSP || WII
35825             case 3:
35826 #if OPENDINGUX
35827                 video_fullscreen_flip();
35828                 break;
35829 #endif
35830 
35831 #if WII
35832                 //video_fullscreen_flip();
35833                 video_stretch((savedata.stretch ^= 1));
35834                 break;
35835 #endif
35836 
35837 #if PSP
35838                 if(videoMode == 0)
35839                 {
35840                     // 320x240
35841                     videomodes.mode += dir;
35842                     if(videomodes.mode > PSP_DISPLAY_FORMATS - 1)
35843                     {
35844                         videomodes.mode = 0;
35845                     }
35846                     if(videomodes.mode < 0)
35847                     {
35848                         videomodes.mode = PSP_DISPLAY_FORMATS - 1;
35849                     }
35850                     video_set_mode(videomodes);
35851                 }
35852                 break;
35853 
35854             case 4:
35855                 if(videoMode == 0)
35856                 {
35857                     // 320x240
35858                     videomodes.filter += dir;
35859                     if(videomodes.filter > PSP_DISPLAY_FILTERS - 1)
35860                     {
35861                         videomodes.filter = 0;
35862                     }
35863                     if(videomodes.filter < 0)
35864                     {
35865                         videomodes.filter = PSP_DISPLAY_FILTERS - 1;
35866                     }
35867                     savedata.swfilter = videomodes.filter;
35868                     video_set_mode(videomodes);
35869                 }
35870                 break;
35871 
35872             case 5:
35873                 displayMode += dir;
35874                 if(displayMode > PSP_DISPLAY_MODES - 1)
35875                 {
35876                     displayMode = 0;
35877                 }
35878                 if(displayMode < 0)
35879                 {
35880                     displayMode = PSP_DISPLAY_MODES - 1;
35881                 }
35882                 if(displayMode)
35883                 {
35884                     setGraphicsTVOverScan(savedata.overscan[0], savedata.overscan[1], savedata.overscan[2], savedata.overscan[3]);
35885                 }
35886                 else
35887                 {
35888                     setGraphicsTVOverScan(0, 0, 0, 0);
35889                 }
35890                 savedata.usetv = displayMode;
35891                 disableGraphics();
35892                 initGraphics(savedata.usetv, videomodes.pixel);
35893                 video_set_mode(videomodes);
35894                 break;
35895             case 6:
35896             case 7:
35897             case 8:
35898             case 9:
35899                 savedata.overscan[selector - 8] += dir;
35900                 if(savedata.overscan[selector - 8] > 99)
35901                 {
35902                     savedata.overscan[selector - 8] = 0;
35903                 }
35904                 if(savedata.overscan[selector - 8] < 0)
35905                 {
35906                     savedata.overscan[selector - 8] = 99;
35907                 }
35908                 if(displayMode)
35909                 {
35910                     setGraphicsTVOverScan(savedata.overscan[0], savedata.overscan[1], savedata.overscan[2], savedata.overscan[3]);
35911                     video_set_mode(videomodes);
35912                 }
35913                 break;
35914 #endif
35915 #endif
35916 
35917 
35918 #if SDL
35919 #if !defined(GP2X) && !defined(OPENDINGUX)
35920                 video_fullscreen_flip();
35921                 break;
35922             case 4:
35923                 savedata.usegl[savedata.fullscreen] ^= 1;
35924                 video_set_mode(videomodes);
35925                 set_color_correction(savedata.gamma, savedata.brightness);
35926                 break;
35927             case 5:
35928 #ifndef ANDROID
35929                 if(savedata.fullscreen)
35930                 {
35931                     break;
35932                 }
35933                 savedata.glscale += dir * 0.25;
35934                 if(savedata.glscale < 0.25)
35935                 {
35936                     savedata.glscale = 0.25;
35937                 }
35938                 if(savedata.glscale > 4.00)
35939                 {
35940                     savedata.glscale = 4.00;
35941                 }
35942                 video_set_mode(videomodes);
35943 #else
35944                 savedata.glscale += dir * 0.25;
35945                 if(savedata.glscale < 0.0)
35946                 {
35947                     savedata.glscale = 0.0;
35948                 }
35949                 if(savedata.glscale > 4.00)
35950                 {
35951                     savedata.glscale = 4.00;
35952                 }
35953 #endif
35954                 break;
35955             case 6:
35956 #ifndef ANDROID
35957                 if(!savedata.fullscreen && savedata.glscale == 1.0)
35958                 {
35959                     break;
35960                 }
35961 #endif
35962                 savedata.glfilter[savedata.fullscreen] += dir;
35963                 if(savedata.glfilter[savedata.fullscreen] < 0)
35964                 {
35965                     savedata.glfilter[savedata.fullscreen] = 1;
35966                 }
35967                 if(savedata.glfilter[savedata.fullscreen] > 1)
35968                 {
35969                     savedata.glfilter[savedata.fullscreen] = 0;
35970                 }
35971                 video_set_mode(videomodes);
35972 				break;
35973             case 7:
35974                 if(!savedata.fullscreen && savedata.glscale < 2.0)
35975                 {
35976                     break;
35977                 }
35978                 videomodes.filter += dir;
35979                 if(videomodes.filter > BLITTER_MAX - 1)
35980                 {
35981                     videomodes.filter = 0;
35982                 }
35983                 if(videomodes.filter < 0)
35984                 {
35985                     videomodes.filter = BLITTER_MAX - 1;
35986                 }
35987                 savedata.swfilter = videomodes.filter;
35988                 memset(pDeltaBuffer, 0x00, 1244160);
35989 				video_set_mode(videomodes);
35990                 break;
35991             case 8:
35992                 video_stretch((savedata.stretch ^= 1));
35993                 break;
35994 #endif
35995 #endif
35996             default:
35997                 quit = 1;
35998             }
35999         }
36000     }
36001     savesettings();
36002     bothnewkeys = 0;
36003     videooptionsMenu = 0;
36004 }
36005 
36006 
menu_options()36007 void menu_options()
36008 {
36009     #define TOT_CHEATS          3
36010     #define OPT_Y_POS          -1
36011     #define OPT_X_POS          -7
36012     #define CHEAT_PAUSE_POSY    3
36013 
36014     typedef enum {
36015         VIDEO_OPTION,
36016         SOUND_OPTION,
36017         CONTROL_OPTION,
36018         SYSTEM_OPTION,
36019 
36020         LIVES_CHEAT,
36021         CREDITS_CHEAT,
36022         HEALTH_CHEAT,
36023 
36024         END_OPTION
36025     } e_selector;
36026 
36027     int quit = 0;
36028     int y_offset = OPT_Y_POS;
36029     int BACK_OPTION = END_OPTION-TOT_CHEATS;
36030     int cheat_opt_offset = 0;
36031     e_selector selector = VIDEO_OPTION;
36032 
36033     optionsMenu = 1;
36034     bothnewkeys = 0;
36035 
36036     if (cheats && !forcecheatsoff)
36037     {
36038         if(level != NULL && pause > 0) y_offset += CHEAT_PAUSE_POSY;
36039         y_offset -= TOT_CHEATS;
36040         cheat_opt_offset += 1;
36041         BACK_OPTION += TOT_CHEATS;
36042     }
36043 
36044     while(!quit)
36045     {
36046         if (!cheats || forcecheatsoff) _menutextm(2, y_offset-1, 0, Tr("Options")); else _menutextm(2, y_offset-1, 0, Tr("Cheat Options"));
36047 
36048         _menutextm((selector == VIDEO_OPTION), y_offset+VIDEO_OPTION, 0, Tr("Video Options..."));
36049         _menutextm((selector == SOUND_OPTION), y_offset+SOUND_OPTION, 0, Tr("Sound Options..."));
36050         _menutextm((selector == CONTROL_OPTION), y_offset+CONTROL_OPTION, 0, Tr("Control Options..."));
36051         _menutextm((selector == SYSTEM_OPTION), y_offset+SYSTEM_OPTION, 0, Tr("System Options..."));
36052 
36053         if (cheats && !forcecheatsoff)
36054         {
36055             _menutext((selector == LIVES_CHEAT), OPT_X_POS, y_offset+cheat_opt_offset+LIVES_CHEAT, (livescheat)?Tr("Infinite Lives On"):Tr("Infinite Lives Off"));
36056             _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
36057             _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
36058         }
36059 
36060         _menutextm((selector == BACK_OPTION), y_offset+cheat_opt_offset+BACK_OPTION+2, 0, Tr("Back"));
36061 
36062         update((level != NULL), 0);
36063 
36064         if(bothnewkeys & FLAG_ESC)
36065         {
36066             quit = 1;
36067         }
36068         if(bothnewkeys & FLAG_MOVEUP)
36069         {
36070             if(selector <= VIDEO_OPTION)
36071             {
36072                 selector = BACK_OPTION;
36073             }
36074             else --selector;
36075 
36076             if(SAMPLE_BEEP >= 0)
36077             {
36078                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
36079             }
36080         }
36081         if(bothnewkeys & FLAG_MOVEDOWN)
36082         {
36083             ++selector;
36084             if(selector > BACK_OPTION)
36085             {
36086                 selector = VIDEO_OPTION;
36087             }
36088 
36089             if(SAMPLE_BEEP >= 0)
36090             {
36091                 sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
36092             }
36093         }
36094         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
36095         {
36096 
36097             if(SAMPLE_BEEP2 >= 0)
36098             {
36099                 sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
36100             }
36101 
36102                 if(selector==BACK_OPTION) quit = 1;
36103            else if(selector==VIDEO_OPTION) menu_options_video();
36104            else if(selector==SOUND_OPTION) menu_options_sound();
36105            else if(selector==CONTROL_OPTION) menu_options_input();
36106            else if(selector==SYSTEM_OPTION)
36107            {
36108                 menu_options_system();
36109                 if (cheats && !forcecheatsoff)
36110                 {
36111                     y_offset = OPT_Y_POS-TOT_CHEATS;
36112                     if(level != NULL && pause > 0) y_offset += CHEAT_PAUSE_POSY;
36113                     cheat_opt_offset = 1;
36114                     BACK_OPTION = END_OPTION-TOT_CHEATS+TOT_CHEATS;
36115                 }
36116                 else
36117                 {
36118                     y_offset = OPT_Y_POS;
36119                     cheat_opt_offset = 0;
36120                     BACK_OPTION = END_OPTION-TOT_CHEATS;
36121                 }
36122            }
36123            else if(selector==LIVES_CHEAT) livescheat = !livescheat;
36124            else if(selector==CREDITS_CHEAT) creditscheat = !creditscheat;
36125            else if(selector==HEALTH_CHEAT) healthcheat = !healthcheat;
36126            else quit = 1;
36127         }
36128     }
36129     savesettings();
36130     if(pause == 1)
36131     {
36132         pause = 2;
36133     }
36134     bothnewkeys = 0;
36135     optionsMenu = 0;
36136 
36137     #undef TOT_CHEATS
36138     #undef OPT_Y_POS
36139     #undef OPT_X_POS
36140     #undef CHEAT_PAUSE_POSY
36141 }
36142 
menu_options_soundcard()36143 void menu_options_soundcard()
36144 {
36145     int quit = 0;
36146     int selector = 0;
36147     int col1 = -8, col2 = 6;
36148 
36149     savesettings();
36150 
36151     bothnewkeys = 0;
36152 
36153     while(!quit)
36154     {
36155         _menutextm(2, -5, 0, Tr("Advanced Sound Options"));
36156         _menutext((selector == 0), col1, -2, Tr("Frequency:"));
36157         _menutext((selector == 0), col2, -2, "%i", savedata.soundrate);
36158         _menutext((selector == 1), col1, -1, Tr("Bits:"));
36159         _menutext((selector == 1), col2, -1, "%i", savedata.soundbits);
36160         _menutextm((selector == 2), 1, 0, Tr("Apply"));
36161         _menutextm((selector == 3), 2, 0, Tr("Discard"));
36162         _menutextm((selector == 4), 7, 0, Tr("Back"));
36163         update((level != NULL), 0);
36164 
36165         if(bothnewkeys & FLAG_ESC)
36166         {
36167             quit = 1;
36168         }
36169         if(bothnewkeys & FLAG_MOVEUP)
36170         {
36171             --selector;
36172             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
36173         }
36174         if(bothnewkeys & FLAG_MOVEDOWN)
36175         {
36176             ++selector;
36177             sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
36178         }
36179         if(selector < 0)
36180         {
36181             selector = 4;
36182         }
36183         selector %= 5;
36184         if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON))
36185         {
36186             sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
36187             switch(selector)
36188             {
36189             case 0:
36190                 if(bothnewkeys & FLAG_MOVELEFT)
36191                 {
36192                     savedata.soundrate >>= 1;
36193                 }
36194                 if(bothnewkeys & FLAG_MOVERIGHT)
36195                 {
36196                     savedata.soundrate <<= 1;
36197                 }
36198                 if(savedata.soundrate < 11025)
36199                 {
36200                     savedata.soundrate = 44100;
36201                 }
36202                 if(savedata.soundrate > 44100)
36203                 {
36204                     savedata.soundrate = 11025;
36205                 }
36206                 break;
36207             case 1:
36208                 savedata.soundbits = (savedata.soundbits ^ (8 + 16));
36209                 if(savedata.soundbits != 8 && savedata.soundbits != 16)
36210                 {
36211                     savedata.soundbits = 8;
36212                 }
36213                 break;
36214             case 2:
36215                 if(!(bothnewkeys & FLAG_ANYBUTTON))
36216                 {
36217                     break;
36218                 }
36219                 // Apply new hardware settings
36220                 sound_stop_playback();
36221                 if(!sound_start_playback(savedata.soundbits, savedata.soundrate))
36222                 {
36223                     savedata.soundbits = 8;
36224                     savedata.soundrate = 11025;
36225                     sound_start_playback(savedata.soundbits, savedata.soundrate);
36226                 }
36227                 music("data/music/remix", 1, 0);
36228                 savesettings();
36229                 break;
36230             case 3:
36231                 if(bothnewkeys & FLAG_ANYBUTTON)
36232                 {
36233                     loadsettings();
36234                 }
36235                 break;
36236             default:
36237                 quit = (bothnewkeys & FLAG_ANYBUTTON);
36238             }
36239         }
36240     }
36241     loadsettings();
36242     bothnewkeys = 0;
36243 }
36244 
36245 // ----------------------------------------------------------------------------
36246 
36247 
openborMain(int argc,char ** argv)36248 void openborMain(int argc, char **argv)
36249 {
36250     sprite_map = NULL;
36251     int quit = 0;
36252     int relback = 1;
36253     int selector = 0;
36254     u32 introtime = 0;
36255     int started = 0;
36256     char tmpBuff[128] = {""};
36257     int players[MAX_PLAYERS] = {0, 0, 0, 0};
36258     int i;
36259     int argl;
36260 
36261     printf("OpenBoR %s, Compile Date: " __DATE__ "\n\n", VERSION);
36262 
36263     if(argc > 1)
36264     {
36265         argl = strlen(argv[1]);
36266         if(argl > 14 && !memcmp(argv[1], "offscreenkill=", 14))
36267         {
36268             DEFAULT_OFFSCREEN_KILL = getValidInt((char *)argv[1] + 14, "", "");
36269         }
36270         if(argl > 14 && !memcmp(argv[1], "showfilesused=", 14))
36271         {
36272             printFileUsageStatistics = getValidInt((char *)argv[1] + 14, "", "");
36273         }
36274     }
36275 
36276 
36277     modelcmdlist = createModelCommandList();
36278     modelstxtcmdlist = createModelstxtCommandList();
36279     levelcmdlist = createLevelCommandList();
36280     levelordercmdlist = createLevelOrderCommandList();
36281     createModelList();
36282 
36283     // Load necessary components.
36284     printf("Game Selected: %s\n\n", packfile);
36285     loadsettings();
36286     startup();
36287 
36288     if(skiptoset < 0)
36289     {
36290 
36291         // New alternative background path for PSP
36292         if(custBkgrds != NULL)
36293         {
36294             strcpy(tmpBuff, custBkgrds);
36295             strncat(tmpBuff, "logo", 4);
36296             load_background(tmpBuff, 0);
36297         }
36298         else
36299         {
36300             load_cached_background("data/bgs/logo", 0);
36301         }
36302 
36303         while(time < GAME_SPEED * 6 && !(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC)))
36304         {
36305             update(0, 0);
36306         }
36307 
36308         music("data/music/remix", 1, 0);
36309 
36310         // New alternative scene path for PSP
36311         if(custScenes != NULL)
36312         {
36313             strncpy(tmpBuff, custScenes, 128);
36314             strncat(tmpBuff, "logo.txt", 8);
36315             playscene(tmpBuff);
36316         }
36317         else
36318         {
36319             playscene("data/scenes/logo.txt");
36320         }
36321     }
36322     clearscreen(background);
36323 
36324     while(!quit)
36325     {
36326         if(skiptoset < 0 && !(goto_mainmenu_flag&8))
36327         {
36328             if(time >= introtime)
36329             {
36330                 // New alternative scene path for PSP
36331                 if(custScenes != NULL)
36332                 {
36333                     strncpy(tmpBuff, custScenes, 128);
36334                     strncat(tmpBuff, "intro.txt", 9);
36335                     playscene(tmpBuff);
36336                 }
36337                 else
36338                 {
36339                     playscene("data/scenes/intro.txt");
36340                 }
36341                 update(0, 0);
36342                 introtime = time + GAME_SPEED * 20;
36343                 relback = 1;
36344                 started = 0;
36345             }
36346 
36347             if(bothnewkeys & FLAG_ESC)
36348             {
36349                 quit = 1;
36350             }
36351         }
36352         else
36353         {
36354             started = 1;
36355             relback = 0;
36356         }
36357 
36358         if (goto_mainmenu_flag != 0) goto_mainmenu_flag = 0;
36359         if(!started)
36360         {
36361             if((time % GAME_SPEED) < (GAME_SPEED / 2))
36362             {
36363                 _menutextm(0, 0, 0, Tr("PRESS START"));
36364             }
36365             if(bothnewkeys & (FLAG_ANYBUTTON))
36366             {
36367                 started = 1;
36368                 relback = 1;
36369             }
36370         }
36371         else if(skiptoset >= 0)
36372         {
36373             loadGameFile();
36374             playgame(players, useSet >= 0 ? useSet : skiptoset, useSave);
36375         }
36376         else
36377         {
36378             _menutextm((selector == 0), 2, 0, Tr("Start Game"));
36379             _menutextm((selector == 1), 3, 0, Tr("Options"));
36380             _menutextm((selector == 2), 4, 0, Tr("How To Play"));
36381             _menutextm((selector == 3), 5, 0, Tr("Hall Of Fame"));
36382             _menutextm((selector == 4), 6, 0, Tr("Quit"));
36383             if(selector < 0)
36384             {
36385                 selector = 4;
36386             }
36387             if(selector > 4)
36388             {
36389                 selector = 0;
36390             }
36391 
36392             if(bothnewkeys)
36393             {
36394                 introtime = time + GAME_SPEED * 20;
36395             }
36396 
36397             if(bothnewkeys & FLAG_MOVEUP)
36398             {
36399                 --selector;
36400                 if(SAMPLE_BEEP >= 0)
36401                 {
36402                     sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
36403                 }
36404             }
36405             if(bothnewkeys & FLAG_MOVEDOWN)
36406             {
36407                 ++selector;
36408                 if(SAMPLE_BEEP >= 0)
36409                 {
36410                     sound_play_sample(SAMPLE_BEEP, 0, savedata.effectvol, savedata.effectvol, 100);
36411                 }
36412             }
36413             if(bothnewkeys & (FLAG_ANYBUTTON))
36414             {
36415                 if(SAMPLE_BEEP2 >= 0)
36416                 {
36417                     sound_play_sample(SAMPLE_BEEP2, 0, savedata.effectvol, savedata.effectvol, 100);
36418                 }
36419                 switch(selector)
36420                 {
36421                 case 0:
36422                     for(i = 0; i < MAX_PLAYERS; i++)
36423                     {
36424                         players[i] = player[i].newkeys & (FLAG_ANYBUTTON);
36425                     }
36426                     relback = choose_mode(players);
36427                     if(relback)
36428                     {
36429                         started = 0;
36430                     }
36431                     break;
36432                 case 1:
36433                     menu_options();
36434                     break;
36435                 case 2:
36436                 {
36437                     int previousLoop = musicloop;
36438                     char previousMusic[sizeof(currentmusic)];
36439                     strncpy(previousMusic, currentmusic, sizeof(previousMusic) - 1);
36440 
36441                     if(custScenes != NULL)
36442                     {
36443                         strncpy(tmpBuff, custScenes, 128);
36444                         strncat(tmpBuff, "howto.txt", 9);
36445                         playscene(tmpBuff);
36446                     }
36447                     else
36448                     {
36449                         playscene("data/scenes/howto.txt");
36450                     }
36451                     if(stricmp(previousMusic, currentmusic) != 0)
36452                     {
36453                         music(previousMusic, previousLoop, 0);
36454                     }
36455                     relback = 1;
36456                     break;
36457                 }
36458                 case 3:
36459                     hallfame(0);
36460                     relback = 1;
36461                     break;
36462                 default:
36463                     quit = 1;
36464                     break;
36465                 }
36466                 introtime = time + GAME_SPEED * 20;
36467             }
36468         }
36469         if(relback)
36470         {
36471             if(started)
36472             {
36473                 menuScreen  = 1;
36474                 titleScreen = 0;
36475                 if(custBkgrds != NULL)
36476                 {
36477                     strncpy(tmpBuff, custBkgrds, 128);
36478                     strncat(tmpBuff, "titleb", 6);
36479                     load_background(tmpBuff, 0);
36480                 }
36481                 else
36482                 {
36483                     load_cached_background("data/bgs/titleb", 0);
36484                 }
36485             }
36486             else
36487             {
36488                 menuScreen  = 0;
36489                 titleScreen = 1;
36490                 if(custBkgrds != NULL)
36491                 {
36492                     strncpy(tmpBuff, custBkgrds, 128);
36493                     strncat(tmpBuff, "title", 5);
36494                     load_background(tmpBuff, 0);
36495                 }
36496                 else
36497                 {
36498                     load_cached_background("data/bgs/title", 0);
36499                 }
36500             }
36501 
36502             if(!sound_query_music(NULL, NULL))
36503             {
36504                 music("data/music/remix", 1, 0);
36505             }
36506             relback = 0;
36507         }
36508         update(0, 0);
36509     }
36510     shutdown(0, DEFAULT_SHUTDOWN_MESSAGE);
36511 }
36512 
is_cheat_actived()36513 int is_cheat_actived()
36514 {
36515     if(!cheats)
36516     {
36517         return 0;
36518     }
36519     else
36520     {
36521         if(!forcecheatsoff)
36522         {
36523             return 1;
36524         }
36525         else
36526         {
36527             return 0;
36528         }
36529     }
36530 }
36531 
36532 #undef GET_ARG
36533 #undef GET_ARG_LEN
36534 #undef GET_ARGP
36535 #undef GET_ARGP_LEN
36536 #undef GET_INT_ARG
36537 #undef GET_FLOAT_ARG
36538 #undef GET_INT_ARGP
36539 #undef GET_FLOAT_ARGP
36540