fix link errors on compilers with strict "extern" enforcement
[rofl0r-openbor.git] / openbor.c
blobcdd0ff43c4b19e2dfa21c2da388c14d59f573187
1 /*
2 * OpenBOR - http://www.LavaLit.com
3 * -----------------------------------------------------------------------
4 * Licensed under the BSD license, see LICENSE in OpenBOR root for details.
6 * Copyright (c) 2004 - 2011 OpenBOR Team
7 */
9 /////////////////////////////////////////////////////////////////////////////
10 // Beats of Rage //
11 // Side-scrolling beat-'em-up //
12 /////////////////////////////////////////////////////////////////////////////
14 #include "debug.h"
15 #include "data.h"
16 #include "openbor.h"
17 #include "commands.h"
18 #include "models.h"
19 #include "movie.h"
20 #include "menus.h"
21 #include "source/strswitch/stringswitch.h"
23 #define GET_ARG(z) arglist.count > z ? arglist.args[z] : ""
24 #define GET_ARG_LEN(z) arglist.count > z ? arglist.arglen[z] : 0
25 #define GET_ARGP(z) arglist->count > z ? arglist->args[z] : ""
26 #define GET_ARGP_LEN(z) arglist->count > z ? arglist->arglen[z] : 0
27 #define GET_INT_ARG(z) getValidInt(GET_ARG(z), filename, command)
28 #define GET_FLOAT_ARG(z) getValidFloat(GET_ARG(z), filename, command)
29 #define GET_INT_ARGP(z) getValidInt(GET_ARGP(z), filename, command)
30 #define GET_FLOAT_ARGP(z) getValidFloat(GET_ARGP(z), filename, command)
32 static const char *E_OUT_OF_MEMORY = "Error: Could not allocate sufficient memory.\n";
33 static int DEFAULT_OFFSCREEN_KILL = 3000;
35 /////////////////////////////////////////////////////////////////////////////
36 // Global Variables //
37 /////////////////////////////////////////////////////////////////////////////
39 s_level_entry *levelorder[MAX_DIFFICULTIES][MAX_LEVELS];
40 s_level *level = NULL;
41 s_screen *vscreen = NULL;
42 s_screen *background = NULL;
43 s_screen *bgbuffer = NULL;
44 char bgbuffer_updated = 0;
45 s_bitmap *texture = NULL;
46 s_videomodes videomodes;
47 s_sprite_list *sprite_list;
48 s_sprite_map *sprite_map;
49 s_anim_list *anim_list;
50 s_modelcache *model_cache;
52 s_player_min_max_z_bgheight player_min_max_z_bgheight = {
53 160, 232, 160
56 char custom_button_names[CB_MAX][16];
57 char disabledkey[CB_MAX] = { 0 };
59 int quit_game = 0;
61 int sprite_map_max_items = 0;
62 int cache_map_max_items = 0;
65 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.
66 List *modelcmdlist = NULL;
67 List* modelsattackcmdlist = NULL;
68 List *modelstxtcmdlist = NULL;
69 List *levelcmdlist = NULL;
70 List *levelordercmdlist = NULL;
71 List *scriptConstantsCommandList = NULL;
74 char *custBkgrds = NULL;
75 char *custLevels = NULL;
76 char *custModels = NULL;
77 char rush_names[2][MAX_NAME_LEN];
78 char branch_name[MAX_NAME_LEN + 1]; // Used for branches
79 char set_names[MAX_DIFFICULTIES][MAX_NAME_LEN + 1];
80 unsigned char pal[MAX_PAL_SIZE] = { "" };
81 int blendfx[MAX_BLENDINGS] = { 0, 1, 0, 0, 0, 0 };
83 char blendfx_is_set = 0;
84 int fontmonospace[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
86 // move all blending effects here
87 unsigned char *blendings[MAX_BLENDINGS] = { NULL, NULL, NULL, NULL, NULL, NULL };
89 // function pointers to create the tables
90 palette_table_function blending_table_functions[MAX_BLENDINGS] =
91 { palette_table_screen, palette_table_multiply, palette_table_overlay,
92 palette_table_hardlight, palette_table_dodge, palette_table_half
96 int current_set = 0;
97 int current_level = 0;
98 int current_stage = 1;
100 float bgtravelled;
101 int traveltime;
102 int texttime;
103 float advancex;
104 float advancey;
106 float scrolldx; // advancex changed previous loop
107 float scrolldy; // advancey .....................
108 float scrollminz; // Limit level z-scroll
109 float scrollmaxz;
110 float blockade; // Limit x scroll back
111 float lasthitx; //Last hit X location.
112 float lasthitz; //Last hit Z location.
113 float lasthita; //Last hit A location.
114 int lasthitt; //Last hit type.
115 int lasthitc; //Last hit confirm (i.e. if engine hit code will be used).
117 // used by gfx shadow
118 int light[2] = { 0, 0 };
120 int shadowalpha = BLEND_MULTIPLY + 1;
122 u32 interval = 0;
123 extern unsigned long seed;
125 s_samples samples = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,};
127 //------------------------------
129 int grab_attacks[5][2] = {
130 {ANI_GRABATTACK, ANI_GRABATTACK2},
131 {ANI_GRABFORWARD, ANI_GRABFORWARD2},
132 {ANI_GRABUP, ANI_GRABUP2},
133 {ANI_GRABDOWN, ANI_GRABDOWN2},
134 {ANI_GRABBACKWARD, ANI_GRABBACKWARD2}
137 // background cache to speed up in-game menus
138 #if WII
139 s_screen *bg_cache[MAX_CACHED_BACKGROUNDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
141 unsigned char bg_palette_cache[MAX_CACHED_BACKGROUNDS][MAX_PAL_SIZE];
142 #endif
144 int maxplayers[MAX_DIFFICULTIES] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
145 int ctrlmaxplayers[MAX_DIFFICULTIES] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
147 unsigned int num_levels[MAX_DIFFICULTIES];
148 unsigned int ifcomplete[MAX_DIFFICULTIES];
149 unsigned int num_difficulties;
150 unsigned int noshowhof[MAX_DIFFICULTIES];
151 unsigned int difflives[MAX_DIFFICULTIES]; // What too easy? change the # of lives players get
152 unsigned int custfade[MAX_DIFFICULTIES];
153 unsigned int diffcreds[MAX_DIFFICULTIES]; // What still to easy - lets see how they do without continues!
154 unsigned int diffoverlap[MAX_DIFFICULTIES]; // Music overlap
155 unsigned int typemp[MAX_DIFFICULTIES];
156 unsigned int continuescore[MAX_DIFFICULTIES]; //what to do with score if continue is used.
157 char *(*skipselect)[MAX_DIFFICULTIES][MAX_PLAYERS] = NULL; // skips select screen and automatically gives players models specified
158 int cansave_flag[MAX_DIFFICULTIES]; // 0, no save, 1 save level position 2 save all: lives/credits/hp/mp/also player
160 int cameratype = 0;
162 u32 go_time = 0;
163 u32 borTime = 0;
164 u32 newtime = 0;
165 unsigned char slowmotion[3] = { 0, 2, 0 }; // [0] = enable/disable; [1] = duration; [2] = counter;
167 int disablelog = 0;
168 #define PLOG(fmt, args...) do { if(!disablelog) fprintf(stdout, fmt, ## args); } while (0)
170 int currentspawnplayer = 0;
171 int MAX_WALL_HEIGHT = 1000; // Max wall height that an entity can be spawned on
172 int saveslot = 0;
173 int current_palette = 0;
174 int fade = 24;
175 int credits = 0;
176 int gosound = 0; // Used to prevent go sound playing too frequently,
177 int musicoverlap = 0;
178 int colorbars = 0;
179 int current_spawn = 0;
180 int level_completed = 0;
181 int nojoin = 0; // dont allow new hero to join in, use "Please Wait" instead of "Select Hero"
182 int groupmin = 0;
183 int groupmax = 0;
184 int selectScreen = 0; // Flag to determine if at select screen (used for setting animations)
185 int tospeedup = 0; // If set will speed the level back up after a boss hits the ground
186 int reached[4] = { 0, 0, 0, 0 }; // Used with TYPE_ENDLEVEL to determine which players have reached the point //4player
188 int noslowfx = 0; // Flag to determine if sound speed when hitting opponent slows or not
189 int equalairpause = 0; // If set to 1, there will be no extra pausetime for players who hit multiple enemies in midair
190 int hiscorebg = 0; // If set to 1, will look for a background image to display at the highscore screen
191 int completebg = 0; // If set to 1, will look for a background image to display at the showcomplete screen
192 s_loadingbar loadingbg[2] = { {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
194 int loadingmusic = 0;
195 int unlockbg = 0; // If set to 1, will look for a different background image after defeating the game
196 int pause = 0;
197 int nopause = 0; // OX. If set to 1 , pausing the game will be disabled.
198 int noscreenshot = 0; // OX. If set to 1 , taking screenshots is disabled.
199 int endgame = 0;
201 int forcecheatsoff = 0;
202 int cheats = 0;
203 int livescheat = 0;
204 int creditscheat = 0;
205 int healthcheat = 0;
207 int keyscriptrate = 0;
209 int showtimeover = 0;
210 int sameplayer = 0; // 7-1-2005 flag to determine if players can use the same character
211 int PLAYER_LIVES = 3; // 7-1-2005 default setting for Lives
212 int CONTINUES = 5; // 7-1-2005 default setting for continues
213 int colourselect = 0; // 6-2-2005 Colour select is optional
214 int autoland = 0; // Default set to no autoland and landing is valid with u j combo
215 int ajspecial = 0; // Flag to determine if holding down attack and pressing jump executes special
216 int nolost = 0; // variable to control if drop weapon when grab a enemy by tails
217 int nocost = 0; // If set, special will not cost life unless an enemy is hit
218 int mpstrict = 0; // If current system will check all animation's energy cost when set new animations
219 int magic_type = 0; // use for restore mp by time by tails
220 entity *textbox = NULL;
221 entity *smartbomber = NULL;
222 int plife[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable player lifebar
223 int plifeX[4][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1} }; // Used for customizable player lifebar 'x'
224 int plifeN[4][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1} }; // Used for customizable player lifebar number of lives
225 int picon[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable player icon
226 int piconw[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable player weapon icons
227 int mpicon[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable magicbar player icon
228 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
229 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
230 int pshoot[4][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1} }; // Used for customizable player shootnum
231 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
232 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
233 char musicname[128] = { "" };
234 float musicfade[2] = { 0, 0 };
236 int musicloop = 0;
237 u32 musicoffset = 0;
239 int timeloc[6] = { 0, 0, 0, 0, 0, -1 }; // Used for customizable timeclock location/size
241 int timeicon = -1;
242 short timeicon_offsets[2] = { 0, 0 };
243 char timeicon_path[128] = { "" };
245 int bgicon = -1;
246 short bgicon_offsets[3] = { 0, 0, 0 };
247 char bgicon_path[128] = { "" };
249 int olicon = -1;
250 short olicon_offsets[3] = { 0, 0, 0 };
251 char olicon_path[128] = { "" };
252 int elife[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable enemy lifebar
253 int ename[4][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1} }; // Used for customizable enemy name
254 int eicon[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable enemy icon
255 int scomplete[6] = { 0, 0, 0, 0, 0, 0 }; // Used for customizable Stage # Complete
256 int cbonus[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Used for customizable clear bonus
257 int lbonus[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Used for customizable life bonus
258 int rbonus[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Used for customizable rush bonus
259 int tscore[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Used for customizable total score
260 int scbonuses[4] = { 10000, 1000, 100, 0 }; //Stage complete bonus multipliers
262 int showrushbonus = 0;
263 int noshare = 0; // Used for when you want to keep p1 & p2 credits separate
264 int nodropen = 0; // Drop or not when spawning is now a modder option
265 int gfx_y_offset = 0;
266 int timeleft = 0;
267 int oldtime = 0; // One second back from time left.
268 int holez = 0; // Used for setting spawn points
269 int allow_secret_chars = 0;
270 unsigned int lifescore = 50000; // Number of points needed to earn a 1-up
271 unsigned int credscore = 0; // Number of points needed to earn a credit
272 int mpblock = 0; // Take chip damage from health or MP first?
273 int blockratio = 0; // Take half-damage while blocking?
274 int nochipdeath = 0; // Prevents entities from dying due to chip damage (damage while blocking)
275 int noaircancel = 0; // Now, you can make jumping attacks uncancellable!
276 int nomaxrushreset[5] = { 0, 0, 0, 0, 0 };
278 int mpbartext[4] = { -1, 0, 0, 0 }; // Array for adjusting MP status text (font, Xpos, Ypos, Display type).
279 int lbartext[4] = { -1, 0, 0, 0 }; // Array for adjusting HP status text (font, Xpos, Ypos, Display type).
280 int pmp[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; // Used for customizable player mpbar
281 int spdirection[4] = { 1, 0, 1, 0 }; // Used for Select Player Direction for select player screen
283 int bonus = 0; // Used for unlocking Bonus difficulties
284 int versusdamage = 2; // Used for setting mode. (ability to hit other players)
285 int same[MAX_DIFFICULTIES]; // ltb 1-13-05 sameplayer
286 int z_coords[3] = { 0, 0, 0 }; // Used for setting customizable walkable area
287 int rush[6] = { 0, 2, 3, 3, 3, 3 };
289 s_colors colors = {0};
291 int lifebarfgalpha = 0;
292 int lifebarbgalpha = 2;
293 int shadowsprites[6] = { -1, -1, -1, -1, -1, -1 };
295 int gosprite = -1;
296 int golsprite = -1;
297 int holesprite = -1;
298 unsigned int videoMode = 0;
299 int scoreformat = 0; // If set fill score values with 6 Zeros
301 // Funny neon lights
302 unsigned char neontable[MAX_PAL_SIZE];
303 unsigned int neon_time = 0;
305 s_panel panels[MAX_PANELS];
306 unsigned int panels_loaded = 0;
307 int panel_width = 0;
308 int panel_height = 0;
310 s_sprite *frontpanels[MAX_PANELS];
311 unsigned int frontpanels_loaded = 0;
313 unsigned int sprites_loaded = 0;
314 unsigned int anims_loaded = 0;
316 unsigned int models_loaded = 0;
317 unsigned int models_cached = 0;
319 entity *ent_list[MAX_ENTS];
320 entity *self;
321 int ent_count = 0; // log count of entites
322 int ent_max = 0;
323 int combodelay = GAME_SPEED / 2;
325 s_player player[4];
326 u32 bothkeys, bothnewkeys;
328 s_playercontrols playercontrols1;
329 s_playercontrols playercontrols2;
330 s_playercontrols playercontrols3;
331 s_playercontrols playercontrols4;
332 s_playercontrols *playercontrolpointers[] = { &playercontrols1, &playercontrols2, &playercontrols3, &playercontrols4 };
334 s_savelevel savelevel[MAX_DIFFICULTIES];
335 s_savescore savescore;
336 s_savedata savedata;
337 s_game_scripts game_scripts;
339 extern Script *pcurrentscript; //used by local script functions
341 void common_walkoff(void);
343 //-------------------------methods-------------------------------
345 #define DEFAULT_SHUTDOWN_MESSAGE \
346 "OpenBOR %s, Compile Date: " __DATE__ "\n" \
347 "Presented by Senile Team.\n" \
348 "This Version is unofficial and based on the Senile source code.\n" \
349 "\n" \
350 "Special thanks to SEGA and SNK.\n\n", \
351 VERSION
353 void leave_game(void) {
354 if(strcmp(packfile, MENU_PACK_FILENAME))
355 saveHighScoreFile();
356 shutdown(0, DEFAULT_SHUTDOWN_MESSAGE);
359 static void setDestIfDestNeg_int(int* dest, int source) {
360 if(*dest < 0) *dest = source;
362 static void setDestIfDestNeg_short(short* dest, short source) {
363 if(*dest < 0) *dest = source;
365 static void setDestIfDestNeg_char(char* dest, char source) {
366 if(*dest < 0) *dest = source;
369 void setDrawMethod(s_anim * a, ptrdiff_t index, s_drawmethod * m) {
370 assert(index >= 0);
371 assert(a != NULL);
372 assert(m != NULL);
373 assert(index < a->numframes);
374 a->drawmethods[index] = m;
377 s_drawmethod *getDrawMethod(s_anim * a, ptrdiff_t index) {
378 assert(index >= 0);
379 assert(a != NULL);
380 assert(index < a->numframes);
381 return a->drawmethods[index];
384 int isLoadingScreenTypeBg(loadingScreenType what) {
385 return (what & LSTYPE_BACKGROUND) == LSTYPE_BACKGROUND;
388 int isLoadingScreenTypeBar(loadingScreenType what) {
389 return (what & LSTYPE_BAR) == LSTYPE_BAR;
392 char *fill_s_loadingbar(s_loadingbar * s, char set, short bx, short by, short bsize, short tx, short ty, char tf,
393 int ms) {
394 switch (set) {
395 case 1:
396 s->set = (LSTYPE_BACKGROUND | LSTYPE_BAR);
397 break;
398 case 2:
399 s->set = LSTYPE_BACKGROUND;
400 break;
401 case 3:
402 s->set = LSTYPE_BAR;
403 break;
404 case 0:
405 s->set = LSTYPE_NONE;
406 break;
407 default:
408 s->set = LSTYPE_NONE;
409 printf("invalid loadingbg type %d!\n", set);
411 s->tf = tf;
412 s->bx = bx;
413 s->by = by;
414 s->bsize = bsize;
415 s->tx = tx;
416 s->ty = ty;
417 s->refreshMs = (ms ? ms : 100);
418 return NULL;
422 // returns: 1 - succeeded 0 - failed
423 int buffer_pakfile(char *filename, char **pbuffer, size_t * psize) {
424 int handle;
425 *psize = 0;
426 *pbuffer = NULL;
427 // Read file
428 PDEBUG("pakfile requested: %s.\n", filename); //ASDF
430 if((handle = openpackfile(filename, packfile)) < 0) {
431 PDEBUG("couldnt get handle!\n");
432 return 0;
434 *psize = seekpackfile(handle, 0, SEEK_END);
435 seekpackfile(handle, 0, SEEK_SET);
437 *pbuffer = (char *) malloc(*psize + 1);
438 if(*pbuffer == NULL) {
439 *psize = 0;
440 closepackfile(handle);
441 shutdown(1, "Can't create buffer for packfile '%s'", filename);
442 return 0;
444 if(readpackfile(handle, *pbuffer, *psize) != *psize) {
445 freeAndNull((void**) pbuffer);
446 *psize = 0;
447 closepackfile(handle);
448 shutdown(1, "Can't read from packfile '%s'", filename);
449 return 0;
451 (*pbuffer)[*psize] = 0; // Terminate string (important!)
452 closepackfile(handle);
453 return 1;
456 //this method is used by script engine, we move it here
457 // it will get a system property, put it in the ScriptVariant
458 // if failed return 0, otherwise return 1
459 int getsyspropertybyindex(ScriptVariant * var, int index) {
460 enum nameenum {
461 _e_branchname,
462 _e_count_enemies,
463 _e_count_entities,
464 _e_count_npcs,
465 _e_count_players,
466 _e_current_level,
467 _e_current_palette,
468 _e_current_set,
469 _e_current_stage,
470 _e_elapsed_time,
471 _e_ent_max,
472 _e_game_paused,
473 _e_game_speed,
474 _e_gfx_y_offset,
475 _e_hResolution,
476 _e_in_level,
477 _e_in_selectscreen,
478 _e_lasthita,
479 _e_lasthitc,
480 _e_lasthitt,
481 _e_lasthitx,
482 _e_lasthitz,
483 _e_levelheight,
484 _e_levelwidth,
485 _e_lightx,
486 _e_lightz,
487 _e_maxentityvars,
488 _e_maxglobalvars,
489 _e_maxindexedvars,
490 _e_maxplayers,
491 _e_maxscriptvars,
492 _e_models_cached,
493 _e_models_loaded,
494 _e_numpalettes,
495 _e_pixelformat,
496 _e_player,
497 _e_player1,
498 _e_player2,
499 _e_player3,
500 _e_player4,
501 _e_player_max_z,
502 _e_player_min_z,
503 _e_shadowalpha,
504 _e_shadowcolor,
505 _e_slowmotion,
506 _e_slowmotion_duration,
507 _e_totalram,
508 _e_freeram,
509 _e_usedram,
510 _e_vResolution,
511 _e_xpos,
512 _e_ypos,
513 _e_the_end,
516 if(!var)
517 return 0;
519 switch (index) {
521 case _e_lasthita: case _e_lasthitx: case _e_lasthitz:
522 case _e_xpos: case _e_ypos:
523 ScriptVariant_ChangeType(var, VT_DECIMAL);
524 switch(index) {
525 case _e_xpos: case _e_ypos:
526 if(!level)
527 return 0;
528 if(index == _e_xpos)
529 var->dblVal = (double) advancex;
530 else
531 var->dblVal = (double) advancey;
532 break;
533 case _e_lasthita: var->dblVal = (double) (lasthita); break;
534 case _e_lasthitx: var->dblVal = (double) (lasthitx); break;
535 case _e_lasthitz: var->dblVal = (double) (lasthitz); break;
536 default:
537 assert(0);
539 break;
540 case _e_branchname:
541 ScriptVariant_ChangeType(var, VT_STR);
542 strcpy(StrCache_Get(var->strVal), branch_name);
543 break;
544 case _e_player: case _e_player1: case _e_player2:
545 case _e_player3: case _e_player4:
546 ScriptVariant_ChangeType(var, VT_PTR);
547 switch(index) {
548 case _e_player: case _e_player1: var->ptrVal = (void*) player; break;
549 case _e_player2: var->ptrVal = (void*) (player + 1); break;
550 case _e_player3: var->ptrVal = (void*) (player + 2); break;
551 case _e_player4: var->ptrVal = (void*) (player + 3); break;
552 default: assert (0);
554 break;
555 case _e_count_enemies: case _e_count_players: case _e_count_npcs: case _e_count_entities:
556 case _e_ent_max: case _e_in_level: case _e_elapsed_time: case _e_in_selectscreen:
557 case _e_lasthitc: case _e_lasthitt: case _e_hResolution: case _e_vResolution:
558 case _e_current_set: case _e_current_level: case _e_current_palette: case _e_current_stage:
559 case _e_maxentityvars: case _e_maxglobalvars: case _e_maxindexedvars: case _e_maxplayers:
560 case _e_maxscriptvars: case _e_models_loaded: case _e_numpalettes: case _e_pixelformat:
561 case _e_player_max_z: case _e_player_min_z: case _e_lightx: case _e_lightz:
562 case _e_shadowalpha: case _e_slowmotion: case _e_slowmotion_duration: case _e_game_paused:
563 case _e_totalram: case _e_freeram: case _e_usedram:
564 ScriptVariant_ChangeType(var, VT_INTEGER);
565 switch (index) {
566 case _e_count_enemies: var->lVal = (s32) count_ents(TYPE_ENEMY); break;
567 case _e_count_players: var->lVal = (s32) count_ents(TYPE_PLAYER); break;
568 case _e_count_npcs: var->lVal = (s32) count_ents(TYPE_NPC); break;
569 case _e_count_entities: var->lVal = (s32) ent_count; break;
570 case _e_ent_max: var->lVal = (s32) ent_max; break;
571 case _e_in_level: var->lVal = (s32) (level == NULL); break;
572 case _e_elapsed_time: var->lVal = (s32) borTime; break;
573 case _e_in_selectscreen: var->lVal = (s32) (selectScreen); break;
574 case _e_lasthitc: var->lVal = (s32) (lasthitc); break;
575 case _e_lasthitt: var->lVal = (s32) (lasthitt); break;
576 case _e_hResolution: var->lVal = (s32) videomodes.hRes; break;
577 case _e_vResolution: var->lVal = (s32) videomodes.vRes; break;
578 case _e_current_set: var->lVal = (s32) (current_set); break;
579 case _e_current_level: var->lVal = (s32) (current_level); break;
580 case _e_current_palette: var->lVal = (s32) (current_palette); break;
581 case _e_current_stage: var->lVal = (s32) (current_stage); break;
582 case _e_maxentityvars: var->lVal = (s32) max_entity_vars; break;
583 case _e_maxglobalvars: var->lVal = (s32) max_global_vars; break;
584 case _e_maxindexedvars: var->lVal = (s32) max_indexed_vars; break;
585 case _e_maxplayers: var->lVal = (s32) maxplayers[current_set]; break;
586 case _e_maxscriptvars: var->lVal = (s32) max_script_vars; break;
587 case _e_models_cached: var->lVal = (s32) models_cached; break;
588 case _e_models_loaded: var->lVal = (s32) models_loaded; break;
589 case _e_numpalettes: var->lVal = (s32) (level->numpalettes); break;
590 case _e_pixelformat: var->lVal = (s32) pixelformat; break;
591 case _e_player_max_z: var->lVal = (s32) (PLAYER_MAX_Z); break;
592 case _e_player_min_z: var->lVal = (s32) (PLAYER_MIN_Z); break;
593 case _e_lightx: var->lVal = (s32) (light[0]); break;
594 case _e_lightz: var->lVal = (s32) (light[1]); break;
595 case _e_shadowalpha: var->lVal = (s32) shadowalpha; break;
596 case _e_shadowcolor: var->lVal = (s32) colors.shadow; break;
597 case _e_slowmotion: var->lVal = (s32) slowmotion[0]; break;
598 case _e_slowmotion_duration: var->lVal = (s32) slowmotion[1]; break;
599 case _e_game_paused: var->lVal = (s32) pause; break;
600 case _e_totalram: var->lVal = 64 * 1024; break;
601 case _e_freeram: var->lVal = 63 * 1024; break;
602 case _e_usedram: var->lVal = 1024; break;
603 default:
604 assert (0); break;
606 break;
608 case _e_game_speed: case _e_gfx_y_offset: case _e_levelwidth: case _e_levelheight:
609 if(!level)
610 return 0;
611 ScriptVariant_ChangeType(var, VT_INTEGER);
612 switch (index) {
613 case _e_game_speed: var->lVal = (s32) GAME_SPEED; break;
614 case _e_gfx_y_offset: var->lVal = (s32) gfx_y_offset; break;
615 case _e_levelwidth: var->lVal = (s32) (level->width); break;
616 case _e_levelheight: var->lVal = (s32) (panel_height); break;
617 default: assert(0); break;
619 break;
620 default:
621 // We use indices now, but players/modders don't need to be exposed
622 // to that implementation detail, so we write "name" and not "index".
623 printf("Unknown system property name.\n");
624 return 0;
626 return 1;
629 // change a system variant, used by script
630 int changesyspropertybyindex(int index, ScriptVariant * value) {
631 //char* tempstr = NULL;
632 s32 ltemp;
633 //double dbltemp;
635 // This enum is replicated in mapstrings_changesystemvariant in
636 // openborscript.c. If you change one, you must change the other as well!!!!
637 enum changesystemvariant_enum {
638 _csv_blockade,
639 _csv_elapsed_time,
640 _csv_lasthita,
641 _csv_lasthitc,
642 _csv_lasthitt,
643 _csv_lasthitx,
644 _csv_lasthitz,
645 _csv_levelpos,
646 _csv_scrollmaxz,
647 _csv_scrollminz,
648 _csv_slowmotion,
649 _csv_slowmotion_duration,
650 _csv_smartbomber,
651 _csv_textbox,
652 _csv_xpos,
653 _csv_ypos,
654 _csv_the_end,
657 switch (index) {
658 case _csv_elapsed_time: case _csv_levelpos: case _csv_xpos:
659 case _csv_ypos: case _csv_scrollminz: case _csv_scrollmaxz:
660 case _csv_blockade: case _csv_slowmotion: case _csv_slowmotion_duration:
661 case _csv_lasthitx: case _csv_lasthita: case _csv_lasthitc:
662 case _csv_lasthitz: case _csv_lasthitt:
663 if(SUCCEEDED(ScriptVariant_IntegerValue(value, &ltemp))) {
664 switch(index) {
665 case _csv_elapsed_time: borTime = (int) ltemp; break;
666 case _csv_levelpos: level->pos = (int) ltemp; break;
667 case _csv_xpos: advancex = (float) ltemp; break;
668 case _csv_ypos: advancey = (float) ltemp; break;
669 case _csv_scrollminz: scrollminz = (float) ltemp; break;
670 case _csv_scrollmaxz: scrollmaxz = (float) ltemp; break;
671 case _csv_blockade: blockade = (float) ltemp; break;
672 case _csv_slowmotion: slowmotion[0] = (unsigned char) ltemp; break;
673 case _csv_slowmotion_duration: slowmotion[1] = (unsigned char) ltemp; break;
674 case _csv_lasthitx: lasthitx = (float) ltemp; break;
675 case _csv_lasthita: lasthita = (float) ltemp; break;
676 case _csv_lasthitc: lasthitc = (int) ltemp; break;
677 case _csv_lasthitz: lasthitz = (float) ltemp; break;
678 case _csv_lasthitt: lasthitt = (int) ltemp; break;
679 default: assert(0);
682 break;
683 case _csv_smartbomber:
684 smartbomber = (entity *) value;
685 break;
686 case _csv_textbox:
687 textbox = (entity *) value;
688 break;
689 default:
690 return 0;
693 return 1;
697 int load_script(Script * script, char *file) {
698 size_t size = 0;
699 int failed = 0;
700 char *buf = NULL;
702 if(buffer_pakfile(file, &buf, &size) != 1)
703 return 0;
705 failed = !Script_AppendText(script, buf, file);
707 freeAndNull((void**) &buf);
708 // text loaded but parsing failed, shutdown
709 if(failed)
710 shutdown(1, "Failed to parse script file: '%s'!\n", file);
711 return !failed;
714 // this method is used by load_scripts, don't call it
715 void init_scripts() {
716 int i;
717 Script_Global_Init();
718 for (i = 0; i < script_and_path_and_name_itemcount; i++) {
719 Script_Init(script_and_path_and_name[i].script, script_and_path_and_name[i].name, 1);
723 // This method is called once when the engine starts, do not use it multiple times
724 // It should be calld after load_script_setting
725 void load_scripts() {
726 int i;
727 init_scripts();
728 //Script_Clear's second parameter set to 2, because the script fails to load,
729 //and will never have another chance to be loaded, so just clear the variable list in it
730 for (i = 0; i < script_and_path_and_name_itemcount; i++) {
731 if(!load_script(script_and_path_and_name[i].script, script_and_path_and_name[i].path))
732 Script_Clear(script_and_path_and_name[i].script, 2);
733 Script_Compile(script_and_path_and_name[i].script);
737 // This method is called once when the engine is shutting down, do not use it multiple times
738 void clear_scripts() {
739 int i;
740 //Script_Clear's second parameter set to 2, because the script fails to load,
741 //and will never have another chance to be loaded, so just clear the variable list in it
742 for(i = 0; i < script_and_path_and_name_itemcount; i++) {
743 Script_Clear(script_and_path_and_name[i].script, 2);
746 Script_Global_Clear();
749 void alloc_all_scripts(s_scripts * s) {
750 static const size_t scripts_membercount = sizeof(s_scripts) / sizeof(Script *);
751 size_t i;
753 for(i = 0; i < scripts_membercount; i++) {
754 (((Script **) s)[i]) = alloc_script();
758 void clear_all_scripts(s_scripts * s, int method) {
759 static const size_t scripts_membercount = sizeof(s_scripts) / sizeof(Script *);
760 size_t i;
761 Script **ps = (Script **) s;
763 for(i = 0; i < scripts_membercount; i++) {
764 Script_Clear(ps[i], method);
768 void free_all_scripts(s_scripts * s) {
769 static const size_t scripts_membercount = sizeof(s_scripts) / sizeof(Script *);
770 size_t i;
771 Script **ps = (Script **) s;
773 for(i = 0; i < scripts_membercount; i++) {
774 freeAndNull((void**) &ps[i]);
778 void copy_all_scripts(s_scripts * src, s_scripts * dest, int method) {
779 static const size_t scripts_membercount = sizeof(s_scripts) / sizeof(Script *);
780 size_t i;
781 Script **ps = (Script **) src;
782 Script **pd = (Script **) dest;
784 for(i = 0; i < scripts_membercount; i++) {
785 Script_Copy(pd[i], ps[i], method);
789 static const s_script_args_names script_args_names = {
790 .ent = "self",
791 .attacker = "attacker",
792 .drop = "drop",
793 .type = "attacktype",
794 .noblock = "noblock",
795 .guardcost = "guardcost",
796 .jugglecost = "jugglecost",
797 .pauseadd = "pauseadd",
798 .which = "which",
799 .atkid = "attackid",
800 .blocked = "blocked",
801 .animnum = "animnum",
802 .frame = "frame",
803 .player = "player",
804 .attacktype = "attacktype",
805 .reset = "reset",
806 .plane = "plane",
807 .height = "height",
808 .obstacle = "obstacle",
809 .time = "time",
810 .gotime = "gotime",
811 .damage = "damage",
812 .damagetaker = "damagetaker",
813 .other = "other",
816 static const s_script_args init_script_args_default = {
817 .ent = {VT_PTR, 0},
818 .attacker = {VT_PTR, 0},
819 .drop = {VT_INTEGER, 0},
820 .type = {VT_INTEGER, 0},
821 .noblock = {VT_INTEGER, 0},
822 .guardcost = {VT_INTEGER, 0},
823 .jugglecost = {VT_INTEGER, 0},
824 .pauseadd = {VT_INTEGER, 0},
825 .which = {VT_EMPTY, 0},
826 .atkid = {VT_EMPTY, 0},
827 .blocked = {VT_EMPTY, 0},
828 .animnum = {VT_EMPTY, 0},
829 .frame = {VT_EMPTY, 0},
830 .player = {VT_EMPTY, 0},
831 .attacktype = {VT_EMPTY, 0},
832 .reset = {VT_EMPTY, 0},
833 .plane = {VT_EMPTY, 0},
834 .height = {VT_EMPTY, 0},
835 .obstacle = {VT_EMPTY, 0},
836 .time = {VT_EMPTY, 0},
837 .gotime = {VT_EMPTY, 0},
838 .damage = {VT_INTEGER, 0},
839 .damagetaker = {VT_EMPTY, 0},
840 .other = {VT_EMPTY, 0},
843 static const s_script_args init_script_args_only_ent = {
844 .ent = {VT_PTR, 0},
845 .attacker = {VT_EMPTY, 0},
846 .drop = {VT_EMPTY, 0},
847 .type = {VT_EMPTY, 0},
848 .noblock = {VT_EMPTY, 0},
849 .guardcost = {VT_EMPTY, 0},
850 .jugglecost = {VT_EMPTY, 0},
851 .pauseadd = {VT_EMPTY, 0},
852 .which = {VT_EMPTY, 0},
853 .atkid = {VT_EMPTY, 0},
854 .blocked = {VT_EMPTY, 0},
855 .animnum = {VT_EMPTY, 0},
856 .frame = {VT_EMPTY, 0},
857 .player = {VT_EMPTY, 0},
858 .attacktype = {VT_EMPTY, 0},
859 .reset = {VT_EMPTY, 0},
860 .plane = {VT_EMPTY, 0},
861 .height = {VT_EMPTY, 0},
862 .obstacle = {VT_EMPTY, 0},
863 .time = {VT_EMPTY, 0},
864 .gotime = {VT_EMPTY, 0},
865 .damage = {VT_EMPTY, 0},
866 .damagetaker = {VT_EMPTY, 0},
867 .other = {VT_EMPTY, 0},
870 static void execute_script_default(s_script_args* args, Script* dest_script) {
871 ScriptVariant tempvar;
872 Script *ptempscript = pcurrentscript;
873 s_script_args_tuple* tuples = (s_script_args_tuple*) args;
874 char** names = (char**) &script_args_names;
875 unsigned i;
876 float tmp_float;
877 if(Script_IsInitialized(dest_script)) {
878 ScriptVariant_Init(&tempvar);
879 for(i = 0; i < s_script_args_membercount; i++) {
880 if(tuples[i].vt != VT_EMPTY) {
881 ScriptVariant_ChangeType(&tempvar, tuples[i].vt);
882 switch(tuples[i].vt) {
883 case VT_PTR:
884 tempvar.ptrVal = (void*) tuples[i].value;
885 break;
886 case VT_INTEGER:
887 tempvar.lVal = (s32) tuples[i].value;
888 break;
889 case VT_DECIMAL:
890 memcpy(&tmp_float, &tuples[i].value, sizeof(float));
891 tempvar.dblVal = (double) tmp_float;
892 break;
893 default:
894 assert(0);
896 Script_Set_Local_Variant(names[i], &tempvar);
900 Script_Execute(dest_script);
901 //clear to save variant space
902 ScriptVariant_Clear(&tempvar);
904 for(i = 0; i < s_script_args_membercount; i++) {
905 if(tuples[i].vt != VT_EMPTY)
906 Script_Set_Local_Variant(names[i], &tempvar);
909 pcurrentscript = ptempscript;
912 static void execute_takedamage_script_i(s_script_args* args) {
913 execute_script_default(args, ((entity*) args->ent.value)->scripts.takedamage_script);
916 static void execute_onfall_script_i(s_script_args* args) {
917 execute_script_default(args, ((entity*) args->ent.value)->scripts.onfall_script);
920 static void execute_ondeath_script_i(s_script_args* args) {
921 execute_script_default(args, ((entity*) args->ent.value)->scripts.ondeath_script);
924 static void execute_didblock_script_i(s_script_args* args) {
925 execute_script_default(args, ((entity*) args->ent.value)->scripts.didblock_script);
928 static void execute_ondoattack_script_i(s_script_args* args) {
929 execute_script_default(args, ((entity*) args->ent.value)->scripts.ondoattack_script);
932 static void execute_didhit_script_i(s_script_args* args) {
933 execute_script_default(args, ((entity*) args->ent.value)->scripts.didhit_script);
936 void execute_takedamage_script(entity * ent, entity * other, int force, int drop, int type, int noblock, int guardcost,
937 int jugglecost, int pauseadd) {
938 s_script_args script_args = init_script_args_default;
939 script_args.ent.value = (intptr_t) ent;
940 script_args.attacker.value = (intptr_t) other;
941 script_args.damage.value = force;
942 script_args.drop.value = drop;
943 script_args.type.value = type;
944 script_args.noblock.value = noblock;
945 script_args.guardcost.value = guardcost;
946 script_args.jugglecost.value = jugglecost;
947 script_args.pauseadd.value = pauseadd;
948 execute_takedamage_script_i(&script_args);
951 void execute_ondeath_script(entity * ent, entity * other, int force, int drop, int type, int noblock, int guardcost,
952 int jugglecost, int pauseadd) {
953 s_script_args script_args = init_script_args_default;
954 script_args.ent.value = (intptr_t) ent;
955 script_args.attacker.value = (intptr_t) other;
956 script_args.damage.value = force;
957 script_args.drop.value = drop;
958 script_args.type.value = type;
959 script_args.noblock.value = noblock;
960 script_args.guardcost.value = guardcost;
961 script_args.jugglecost.value = jugglecost;
962 script_args.pauseadd.value = pauseadd;
963 execute_ondeath_script_i(&script_args);
965 void execute_onfall_script(entity * ent, entity * other, int force, int drop, int type, int noblock, int guardcost,
966 int jugglecost, int pauseadd) {
967 s_script_args script_args = init_script_args_default;
968 script_args.ent.value = (intptr_t) ent;
969 script_args.attacker.value = (intptr_t) other;
970 script_args.damage.value = force;
971 script_args.drop.value = drop;
972 script_args.type.value = type;
973 script_args.noblock.value = noblock;
974 script_args.guardcost.value = guardcost;
975 script_args.jugglecost.value = jugglecost;
976 script_args.pauseadd.value = pauseadd;
977 execute_onfall_script_i(&script_args);
980 void execute_didblock_script(entity * ent, entity * other, int force, int drop, int type, int noblock, int guardcost,
981 int jugglecost, int pauseadd) {
982 s_script_args script_args = init_script_args_default;
983 script_args.ent.value = (intptr_t) ent;
984 script_args.attacker.value = (intptr_t) other;
985 script_args.damage.value = force;
986 script_args.drop.value = drop;
987 script_args.type.value = type;
988 script_args.noblock.value = noblock;
989 script_args.guardcost.value = guardcost;
990 script_args.jugglecost.value = jugglecost;
991 script_args.pauseadd.value = pauseadd;
992 execute_didblock_script_i(&script_args);
994 void execute_ondoattack_script(entity * ent, entity * other, int force, int drop, int type, int noblock, int guardcost,
995 int jugglecost, int pauseadd, int iWhich, int iAtkID) {
996 s_script_args script_args = init_script_args_default;
997 script_args.ent.value = (intptr_t) ent;
999 script_args.attacker.vt = VT_EMPTY;
1000 script_args.other.vt = VT_PTR;
1001 script_args.other.value = (intptr_t) other;
1002 /* yep, that one calls it "other", all the others "attacker" */
1004 script_args.damage.value = force;
1005 script_args.drop.value = drop;
1006 script_args.type.value = type;
1007 script_args.noblock.value = noblock;
1008 script_args.guardcost.value = guardcost;
1009 script_args.jugglecost.value = jugglecost;
1010 script_args.pauseadd.value = pauseadd;
1012 script_args.which.vt = VT_INTEGER;
1013 script_args.atkid.vt = VT_INTEGER;
1015 script_args.which.value = iWhich;
1016 script_args.atkid.value = iAtkID;
1018 execute_ondoattack_script_i(&script_args);
1020 void execute_didhit_script(entity * ent, entity * other, int force, int drop, int type, int noblock, int guardcost,
1021 int jugglecost, int pauseadd, int blocked) {
1022 s_script_args script_args = init_script_args_default;
1023 script_args.ent.value = (intptr_t) ent;
1024 script_args.attacker.vt = VT_EMPTY; /* yep, some smartass introduced another field "damagetaker"
1025 instead of reusing "attacker" */
1026 script_args.damagetaker.vt = VT_PTR;
1027 script_args.damagetaker.value = (intptr_t) other;
1029 script_args.blocked.vt = VT_INTEGER;
1030 script_args.blocked.value = blocked;
1032 // at least these few are standard...
1033 script_args.damage.value = force;
1034 script_args.drop.value = drop;
1035 script_args.type.value = type;
1036 script_args.noblock.value = noblock;
1037 script_args.guardcost.value = guardcost;
1038 script_args.jugglecost.value = jugglecost;
1039 script_args.pauseadd.value = pauseadd;
1041 execute_didhit_script_i(&script_args);
1044 static void execute_onblocks_script_i(s_script_args* args) {
1045 execute_script_default(args, ((entity*) args->ent.value)->scripts.onblocks_script);
1047 static void execute_onblockz_script_i(s_script_args* args) {
1048 execute_script_default(args, ((entity*) args->ent.value)->scripts.onblockz_script);
1050 static void execute_onmovex_script_i(s_script_args* args) {
1051 execute_script_default(args, ((entity*) args->ent.value)->scripts.onmovex_script);
1053 static void execute_onmovez_script_i(s_script_args* args) {
1054 execute_script_default(args, ((entity*) args->ent.value)->scripts.onmovez_script);
1056 static void execute_onmovea_script_i(s_script_args* args) {
1057 execute_script_default(args, ((entity*) args->ent.value)->scripts.onmovea_script);
1059 static void execute_onkill_script_i(s_script_args* args) {
1060 execute_script_default(args, ((entity*) args->ent.value)->scripts.onkill_script);
1062 static void execute_updateentity_script_i(s_script_args* args) {
1063 execute_script_default(args, ((entity*) args->ent.value)->scripts.update_script);
1065 static void execute_think_script_i(s_script_args* args) {
1066 execute_script_default(args, ((entity*) args->ent.value)->scripts.think_script);
1068 static void execute_onspawn_script_i(s_script_args* args) {
1069 execute_script_default(args, ((entity*) args->ent.value)->scripts.onspawn_script);
1071 static void execute_animation_script_i(s_script_args* args) {
1072 execute_script_default(args, ((entity*) args->ent.value)->scripts.animation_script);
1074 static void execute_entity_key_script_i(s_script_args* args) {
1075 execute_script_default(args, ((entity*) args->ent.value)->scripts.key_script);
1077 static void execute_onpain_script_i(s_script_args* args) {
1078 execute_script_default(args, ((entity*) args->ent.value)->scripts.onpain_script);
1080 static void execute_onblockw_script_i(s_script_args* args) {
1081 execute_script_default(args, ((entity*) args->ent.value)->scripts.onblockw_script);
1083 static void execute_onblocko_script_i(s_script_args* args) {
1084 execute_script_default(args, ((entity*) args->ent.value)->scripts.onblocko_script);
1086 static void execute_onblocka_script_i(s_script_args* args) {
1087 execute_script_default(args, ((entity*) args->ent.value)->scripts.onblocka_script);
1090 void execute_animation_script(entity * ent) {
1091 s_script_args script_args = init_script_args_only_ent;
1092 script_args.ent.value = (intptr_t) ent;
1093 script_args.animnum.vt = VT_INTEGER;
1094 script_args.frame.vt = VT_INTEGER;
1095 script_args.animnum.value = ent->animnum;
1096 script_args.frame.value = ent->animpos;
1097 execute_animation_script_i(&script_args);
1100 void execute_onblocks_script(entity * ent) {
1101 s_script_args script_args = init_script_args_only_ent;
1102 script_args.ent.value = (intptr_t) ent;
1103 execute_onblocks_script_i(&script_args);
1106 void execute_onblockz_script(entity * ent) {
1107 s_script_args script_args = init_script_args_only_ent;
1108 script_args.ent.value = (intptr_t) ent;
1109 execute_onblockz_script_i(&script_args);
1112 void execute_onmovex_script(entity * ent) {
1113 s_script_args script_args = init_script_args_only_ent;
1114 script_args.ent.value = (intptr_t) ent;
1115 execute_onmovex_script_i(&script_args);
1118 void execute_onmovez_script(entity * ent) {
1119 s_script_args script_args = init_script_args_only_ent;
1120 script_args.ent.value = (intptr_t) ent;
1121 execute_onmovez_script_i(&script_args);
1124 void execute_onmovea_script(entity * ent) {
1125 s_script_args script_args = init_script_args_only_ent;
1126 script_args.ent.value = (intptr_t) ent;
1127 execute_onmovea_script_i(&script_args);
1130 void execute_onkill_script(entity * ent) {
1131 s_script_args script_args = init_script_args_only_ent;
1132 script_args.ent.value = (intptr_t) ent;
1133 execute_onkill_script_i(&script_args);
1136 void execute_updateentity_script(entity * ent) {
1137 s_script_args script_args = init_script_args_only_ent;
1138 script_args.ent.value = (intptr_t) ent;
1139 execute_updateentity_script_i(&script_args);
1142 void execute_think_script(entity * ent) {
1143 s_script_args script_args = init_script_args_only_ent;
1144 script_args.ent.value = (intptr_t) ent;
1145 execute_think_script_i(&script_args);
1148 void execute_onspawn_script(entity * ent) {
1149 s_script_args script_args = init_script_args_only_ent;
1150 script_args.ent.value = (intptr_t) ent;
1151 execute_onspawn_script_i(&script_args);
1154 void execute_entity_key_script(entity * ent) {
1155 s_script_args script_args = init_script_args_only_ent;
1156 script_args.ent.value = (intptr_t) ent;
1157 script_args.player.vt = VT_INTEGER;
1158 script_args.player.value = ent->playerindex;
1159 execute_entity_key_script_i(&script_args);
1162 void execute_onpain_script(entity * ent, int iType, int iReset) {
1163 s_script_args script_args = init_script_args_only_ent;
1164 script_args.ent.value = (intptr_t) ent;
1165 script_args.reset.vt = VT_INTEGER;
1166 script_args.attacktype.vt = VT_INTEGER;
1167 script_args.reset.value = iReset;
1168 script_args.attacktype.value = iType;
1170 /*script_args.type.vt = VT_INTEGER;
1171 script_args.type.value = iReset; */
1173 FIXME the original code did not set this, but did Script_Set_Local_Variant("type", &tempvar)
1174 after setting lval to iReset, additionally it did not set VT_INTEGER on any var */
1175 execute_onpain_script_i(&script_args);
1178 void execute_onblockw_script(entity * ent, int plane, float height) {
1179 s_script_args script_args = init_script_args_only_ent;
1180 script_args.ent.value = (intptr_t) ent;
1181 script_args.plane.vt = VT_INTEGER;
1182 script_args.height.vt = VT_DECIMAL;
1183 script_args.plane.value = plane;
1184 memcpy(&script_args.height.value, &height, sizeof(float));
1185 execute_onblockw_script_i(&script_args);
1188 void execute_onblocko_script(entity * ent, entity * other) {
1189 s_script_args script_args = init_script_args_only_ent;
1190 script_args.ent.value = (intptr_t) ent;
1191 script_args.obstacle.vt = VT_PTR;
1192 script_args.obstacle.value = (intptr_t) other;
1193 execute_onblocko_script_i(&script_args);
1197 void execute_onblocka_script(entity * ent, entity * other) {
1198 s_script_args script_args = init_script_args_only_ent;
1199 script_args.ent.value = (intptr_t) ent;
1200 script_args.obstacle.vt = VT_PTR;
1201 script_args.obstacle.value = (intptr_t) other;
1202 execute_onblocka_script_i(&script_args);
1205 void execute_spawn_script(s_spawn_entry * p, entity * e) {
1206 s_spawn_script_list_node *tempnode = p->spawn_script_list_head;
1207 ScriptVariant tempvar;
1208 Script *ptempscript = pcurrentscript;
1209 while(tempnode) {
1210 pcurrentscript = tempnode->spawn_script;
1211 if(e) {
1212 ScriptVariant_Init(&tempvar);
1213 ScriptVariant_ChangeType(&tempvar, VT_PTR);
1214 tempvar.ptrVal = (void*) e;
1215 Script_Set_Local_Variant("self", &tempvar);
1217 Script_Execute(tempnode->spawn_script);
1218 if(e) {
1219 ScriptVariant_Clear(&tempvar);
1220 Script_Set_Local_Variant("self", &tempvar);
1222 tempnode = tempnode->next;
1224 pcurrentscript = ptempscript;
1227 void execute_script_player(int player_nr, Script* script) {
1228 s_script_args script_args = init_script_args_only_ent;
1229 script_args.ent.vt = VT_EMPTY;
1230 script_args.player.vt = VT_INTEGER;
1231 script_args.player.value = player_nr;
1232 execute_script_default(&script_args, script);
1235 void execute_level_key_script(int player_nr) {
1236 execute_script_player(player_nr, &level->key_script);
1239 void execute_key_script_all(int player_nr) {
1240 execute_script_player(player_nr, &game_scripts.key_script_all);
1243 void execute_timetick_script(int time, int gotime) {
1244 s_script_args script_args = init_script_args_only_ent;
1245 script_args.ent.vt = VT_EMPTY;
1246 script_args.time.vt = VT_INTEGER;
1247 script_args.gotime.vt = VT_INTEGER;
1248 script_args.time.value = time;
1249 script_args.gotime.value = gotime;
1250 execute_script_default(&script_args, &game_scripts.timetick_script);
1253 void execute_key_script(int index) {
1254 Script *ptempscript = pcurrentscript;
1255 if(Script_IsInitialized(&game_scripts.key_script[index])) {
1256 Script_Execute(&game_scripts.key_script[index]);
1258 pcurrentscript = ptempscript;
1261 void execute_join_script(int index) {
1262 Script *ptempscript = pcurrentscript;
1263 if(Script_IsInitialized(&game_scripts.join_script[index])) {
1264 Script_Execute(&game_scripts.join_script[index]);
1266 pcurrentscript = ptempscript;
1269 void execute_respawn_script(int index) {
1270 Script *ptempscript = pcurrentscript;
1271 if(Script_IsInitialized(&game_scripts.respawn_script[index])) {
1272 Script_Execute(&game_scripts.respawn_script[index]);
1274 pcurrentscript = ptempscript;
1277 void execute_pdie_script(int index) {
1278 Script *ptempscript = pcurrentscript;
1279 if(Script_IsInitialized(&game_scripts.pdie_script[index])) {
1280 Script_Execute(&game_scripts.pdie_script[index]);
1282 pcurrentscript = ptempscript;
1285 // ------------------------ Save/load -----------------------------
1287 void clearsettings(void) {
1288 savedata = savedata_default;
1291 void save(char* dest, char* buf, size_t size) {
1292 int disCcWarns;
1293 FILE *handle = NULL;
1294 char path[128] = { "" };
1295 getBasePath(path, "Saves", 0);
1296 strncat(path, dest, 128);
1297 handle = fopen(path, "wb");
1298 if(handle == NULL)
1299 return;
1300 disCcWarns = fwrite(&savedata, 1, sizeof(s_savedata), handle);
1301 fclose(handle);
1304 void savesettings(void) {
1305 char tmpname[128] = { "" };
1306 getSaveFileName(tmpname, ST_CFG);
1307 save(tmpname, (char*) &savedata, sizeof(s_savedata));
1310 void saveasdefault(void) {
1311 save("default.cfg", (char*) &savedata, sizeof(s_savedata));
1314 void saveGameFile(void) {
1315 char tmpname[256] = { "" };
1316 getSaveFileName(tmpname, ST_SAVE);
1317 save(tmpname, (char*) &savelevel, sizeof(s_savelevel) * MAX_DIFFICULTIES);
1320 void saveHighScoreFile(void) {
1321 char tmpname[256] = { "" };
1322 getSaveFileName(tmpname, ST_HISCORE);
1323 save(tmpname, (char*) &savescore, sizeof(s_savescore));
1326 void saveScriptFile(void) {
1327 int disCcWarns;
1328 FILE *handle = NULL;
1329 int i, l, c;
1330 char path[256] = { "" };
1331 char tmpname[256] = { "" };
1332 //named list
1333 //if(max_global_vars<=0) return ;
1334 getBasePath(path, "Saves", 0);
1335 getSaveFileName(tmpname, ST_SCRIPT);
1336 strcat(path, tmpname);
1337 l = strlen(path); //s00, s01, s02 etc
1338 path[l - 2] = '0' + (current_set / 10);
1339 path[l - 1] = '0' + (current_set % 10);
1340 handle = fopen(path, "wb");
1341 if(handle == NULL)
1342 return;
1343 //global variables count
1344 for(i = 0, c = 0; i <= max_global_var_index; i++) {
1345 if(!global_var_list[i]->owner)
1346 c++;
1348 disCcWarns = fwrite(&c, sizeof(c), 1, handle);
1349 for(i = 0; i <= max_global_var_index; i++) {
1350 if(!global_var_list[i]->owner)
1351 disCcWarns = fwrite(global_var_list[i], sizeof(s_variantnode), 1, handle);
1353 // indexed list
1354 if(max_indexed_vars <= 0)
1355 goto CLOSEF;
1356 disCcWarns = fwrite(indexed_var_list + i, sizeof(ScriptVariant), max_indexed_vars, handle);
1357 CLOSEF:
1358 fclose(handle);
1361 // TODO: omg, fix this
1362 // TODO: omg, fix this
1363 // TODO: omg, fix this
1364 // TODO: omg, fix this
1366 void loadsettings(void) {
1367 int disCcWarns;
1368 FILE *handle = NULL;
1369 char path[128] = { "" };
1370 char tmpname[128] = { "" };
1371 getBasePath(path, "Saves", 0);
1372 getSaveFileName(tmpname, ST_CFG);
1373 strcat(path, tmpname);
1374 if(!(fileExists(path))) {
1375 loadfromdefault();
1376 return;
1378 clearsettings();
1379 handle = fopen(path, "rb");
1380 if(handle == NULL)
1381 return;
1382 disCcWarns = fread(&savedata, 1, sizeof(s_savedata), handle);
1383 fclose(handle);
1384 if(savedata.compatibleversion != COMPATIBLEVERSION)
1385 clearsettings();
1388 void loadfromdefault(void) {
1389 int disCcWarns;
1390 FILE *handle = NULL;
1391 char path[128] = { "" };
1392 getBasePath(path, "Saves", 0);
1393 strncat(path, "default.cfg", 128);
1394 clearsettings();
1395 handle = fopen(path, "rb");
1396 if(handle == NULL)
1397 return;
1398 disCcWarns = fread(&savedata, 1, sizeof(s_savedata), handle);
1399 fclose(handle);
1400 if(savedata.compatibleversion != COMPATIBLEVERSION)
1401 clearsettings();
1406 int loadGameFile(void) {
1407 int disCcWarns;
1408 FILE *handle = NULL;
1409 int i;
1410 char path[256] = { "" };
1411 char tmpname[256] = { "" };
1412 getBasePath(path, "Saves", 0);
1413 getSaveFileName(tmpname, ST_SAVE);
1414 strcat(path, tmpname);
1415 handle = fopen(path, "rb");
1416 if(handle == NULL)
1417 return 0;
1418 disCcWarns = fread(&savelevel, sizeof(s_savelevel), MAX_DIFFICULTIES, handle);
1419 fclose(handle);
1420 for(i = 0; i < MAX_DIFFICULTIES; i++)
1421 if(savelevel[i].compatibleversion != CV_SAVED_GAME)
1422 clearSavedGame();
1423 return 1;
1426 void loadHighScoreFile(void) {
1427 int disCcWarns;
1428 FILE *handle = NULL;
1429 char path[256] = { "" };
1430 char tmpname[256] = { "" };
1431 getBasePath(path, "Saves", 0);
1432 getSaveFileName(tmpname, ST_HISCORE);
1433 strcat(path, tmpname);
1434 clearHighScore();
1435 handle = fopen(path, "rb");
1436 if(handle == NULL)
1437 return;
1438 disCcWarns = fread(&savescore, 1, sizeof(s_savescore), handle);
1439 fclose(handle);
1440 if(savescore.compatibleversion != CV_HIGH_SCORE)
1441 clearHighScore();
1446 void loadScriptFile(void) {
1447 int disCcWarns;
1449 size_t size;
1450 ptrdiff_t l, c;
1452 FILE *handle = NULL;
1453 char path[256] = { "" };
1454 char tmpname[256] = { "" };
1455 //named list
1456 //if(max_global_vars<=0) return ;
1457 getBasePath(path, "Saves", 0);
1458 getSaveFileName(tmpname, ST_SCRIPT);
1459 strcat(path, tmpname);
1460 l = strlen(path); //s00, s01, s02 etc
1461 path[l - 2] = '0' + (current_set / 10);
1462 path[l - 1] = '0' + (current_set % 10);
1463 handle = fopen(path, "rb");
1464 if(handle == NULL)
1465 return;
1466 fseek(handle, 0, SEEK_END);
1467 size = ftell(handle);
1468 fseek(handle, 0, SEEK_SET);
1469 if(size < sizeof(c)) {
1470 fclose(handle);
1471 return;
1473 disCcWarns = fread(&c, sizeof(c), 1, handle);
1474 max_global_var_index = c;
1475 if(max_global_var_index >= max_global_vars)
1476 max_global_var_index = max_global_vars - 1;
1477 for(size = 0; size <= max_global_var_index; size++) {
1478 disCcWarns = fread(global_var_list[size], sizeof(s_variantnode), 1, handle);
1480 //indexed list
1481 if(max_indexed_vars <= 0) {
1482 fclose(handle);
1483 return;
1485 size -= ftell(handle);
1486 if(size > sizeof(ScriptVariant) * max_indexed_vars)
1487 size = sizeof(ScriptVariant) * max_indexed_vars;
1488 disCcWarns = fread(indexed_var_list, size, 1, handle);
1489 fclose(handle);
1492 void clearSavedGame(void) {
1493 int i;
1495 for(i = 0; i < MAX_DIFFICULTIES; i++) {
1496 memset(savelevel + i, 0, sizeof(s_savelevel));
1497 savelevel[i].compatibleversion = CV_SAVED_GAME;
1501 void clearHighScore(void) {
1502 unsigned i;
1503 savescore.compatibleversion = CV_HIGH_SCORE;
1505 for(i = 0; i < 10; i++) {
1506 savescore.highsc[i] = 0;
1507 strcpy(savescore.hscoren[i], "None");
1513 // ----------------------- Sound ------------------------------
1515 int music(char *filename, int loop, long offset) {
1516 char t[64];
1517 char a[64];
1518 int res = 1;
1519 if(!savedata.usemusic)
1520 return 0;
1521 if(!sound_open_music(filename, packfile, savedata.musicvol, loop, offset)) {
1522 printf("\nCan't play music file '%s'\n", filename);
1523 res = 0;
1525 if(savedata.showtitles && sound_query_music(a, t)) {
1526 if(a[0] && t[0])
1527 debug_printf("Playing \"%s\" by %s", t, a);
1528 else if(a[0])
1529 debug_printf("Playing unknown song by %s", a);
1530 else if(t[0])
1531 debug_printf("Playing \"%s\" by unknown artist", t);
1532 else
1533 debug_printf("");
1535 return res;
1538 void check_music() {
1539 if(musicfade[1] > 0) {
1540 musicfade[1] -= musicfade[0];
1541 sound_volume_music((int) musicfade[1], (int) musicfade[1]);
1542 } else if(musicname[0]) {
1543 sound_volume_music(savedata.musicvol, savedata.musicvol);
1544 music(musicname, musicloop, musicoffset);
1545 musicname[0] = 0;
1549 // ----------------------- General ------------------------------
1550 // atof and atoi return a valid number, if only the first char is one.
1551 // so we only check that.
1552 int isNumeric(char *text) {
1553 char *p = text;
1554 assert(p);
1555 if(!*p)
1556 return 0;
1557 switch (*p) {
1558 case '-':
1559 case '+':
1560 p++;
1561 break;
1562 default:
1563 break;
1565 switch (*p) {
1566 case '0':
1567 case '1':
1568 case '2':
1569 case '3':
1570 case '4':
1571 case '5':
1572 case '6':
1573 case '7':
1574 case '8':
1575 case '9':
1576 return 1;
1577 default:
1578 return 0;
1580 return 1;
1584 int getValidInt(char *text, char *file, char *cmd) {
1585 static const char *WARN_NUMBER_EXPECTED =
1586 "WARNING: %s tries to load a nonnumeric value at %s, where a number is expected!\nerroneus string: %s\n";
1587 if(!text || !*text)
1588 return 0;
1589 if(isNumeric(text)) {
1590 return atoi(text);
1591 } else {
1592 printf(WARN_NUMBER_EXPECTED, file, cmd, text);
1593 return 0;
1598 float getValidFloat(char *text, char *file, char *cmd) {
1599 static const char *WARN_NUMBER_EXPECTED =
1600 "WARNING: %s tries to load a nonnumeric value at %s, where a number is expected!\nerroneus string: %s\n";
1601 if(!text || !*text)
1602 return 0.0f;
1603 if(isNumeric(text)) {
1604 return atof(text);
1605 } else {
1606 printf(WARN_NUMBER_EXPECTED, file, cmd, text);
1607 return 0.0f;
1611 size_t ParseArgs(ArgList * list, char *input, char *output) {
1612 assert(list);
1613 assert(input);
1614 assert(output);
1615 //static const char diff = 'a' - 'A';
1617 size_t pos = 0;
1618 size_t wordstart = 0;
1619 size_t item = 0;
1620 int done = 0;
1621 int space = 0;
1622 //int makelower = 0;
1624 while(pos < MAX_ARG_LEN - 1 && item < 18) {
1625 switch (input[pos]) {
1626 case '\r':
1627 case '\n':
1628 case '#':
1629 case '\0':
1630 done = 1;
1631 case ' ':
1632 case '\t':
1633 output[pos] = '\0';
1634 if(!space && wordstart != pos) {
1635 list->args[item] = output + wordstart;
1636 list->arglen[item] = pos - wordstart;
1637 item++;
1639 space = 1;
1640 break; /*
1641 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
1642 case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
1643 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
1644 makelower = 1; */
1646 default:
1647 if(space)
1648 wordstart = pos;
1649 /*output[pos] = makelower ? input[pos] + diff : input[pos]; */
1650 output[pos] = input[pos];
1651 space = 0;
1652 //makelower = 0;
1654 if(done)
1655 break;
1656 pos++;
1658 list->count = item;
1659 return item;
1662 char *findarg(char *command, int which) {
1663 static const char comment_mark[4] = { "#" };
1664 int d;
1665 int argc;
1666 int inarg;
1667 int argstart;
1668 static char arg[MAX_ARG_LEN];
1671 // Copy the command line, replacing spaces by zeroes,
1672 // finally returning a pointer to the requested arg.
1673 d = 0;
1674 inarg = 0;
1675 argstart = 0;
1676 argc = -1;
1678 while(d < MAX_ARG_LEN - 1 && command[d]) {
1679 // Zero out whitespace
1680 if(command[d] == ' ' || command[d] == '\t') {
1681 arg[d] = 0;
1682 inarg = 0;
1683 if(argc == which)
1684 return arg + argstart;
1685 } else if(command[d] == 0 || command[d] == '\n' || command[d] == '\r' ||
1686 (!strcmp(command + d, comment_mark))) {
1687 // End of line
1688 arg[d] = 0;
1689 if(argc == which)
1690 return arg + argstart;
1691 return arg + d;
1692 } else {
1693 if(!inarg) {
1694 // if(argc==-1 && command[d]=='#') return arg;
1695 inarg = 1;
1696 argstart = d;
1697 argc++;
1699 arg[d] = command[d];
1701 ++d;
1704 return arg;
1710 float diff(float a, float b) {
1711 if(a < b)
1712 return b - a;
1713 return a - b;
1718 int inair(entity * e) {
1719 return (diff(e->a, e->base) >= 0.1);
1724 float randf(float max) {
1725 float f;
1726 if(max == 0)
1727 return 0;
1728 f = (float) (rand32() % 1000);
1729 f /= (1000 / max);
1730 return f;
1735 // ----------------------- Loaders ------------------------------
1738 // Creates a remapping table from two images
1739 int load_colourmap(s_model * model, char *image1, char *image2) {
1740 int i, j, k;
1741 unsigned char *map = NULL;
1742 s_bitmap *bitmap1 = NULL;
1743 s_bitmap *bitmap2 = NULL;
1745 // Can't use same image twice!
1746 if(stricmp(image1, image2) == 0)
1747 return 0;
1749 // Find an empty slot... ;)
1750 for(k = 0; k < MAX_COLOUR_MAPS && model->colourmap[k]; k++) ;
1751 if(k >= MAX_COLOUR_MAPS)
1752 return -1;
1754 if((map = malloc(MAX_PAL_SIZE / 4)) == NULL) {
1755 return -2;
1757 if((bitmap1 = loadbitmap(image1, packfile, PIXEL_8)) == NULL) {
1758 freeAndNull((void**) &map);
1759 return -3;
1761 if((bitmap2 = loadbitmap(image2, packfile, PIXEL_8)) == NULL) {
1762 freebitmap(bitmap1);
1763 freeAndNull((void**) &map);
1764 return -4;
1766 // Create the colour map
1767 for(i = 0; i < MAX_PAL_SIZE / 4; i++)
1768 map[i] = i;
1769 for(j = 0; j < bitmap1->height && j < bitmap2->height; j++) {
1770 for(i = 0; i < bitmap1->width && i < bitmap2->width; i++) {
1771 map[(unsigned) (bitmap1->data[j * bitmap1->width + i])] = bitmap2->data[j * bitmap2->width + i];
1775 freebitmap(bitmap1);
1776 freebitmap(bitmap2);
1778 model->colourmap[k] = map;
1779 model->maps_loaded = k + 1;
1780 return 1;
1783 //PIXEL_x8
1784 // This function is used to enable remap command in 24bit mode
1785 // So old mods can still run under 16/24/32bit color system
1786 // This function should be called when all colourmaps are loaded, e.g.,
1787 // at the end of load_cached_model
1788 // map flag is used to determine whether a colourmap is a real colourmap
1789 int convert_map_to_palette(s_model * model, unsigned char mapflag[]) {
1790 int i, c;
1791 unsigned char *newmap, *oldmap;
1792 unsigned char *p1, *p2;
1793 unsigned pb = pixelbytes[(int) screenformat];
1794 if(model->palette == NULL)
1795 return 0;
1796 for(c = 0; c < model->maps_loaded; c++) {
1797 if(mapflag[c] == 0)
1798 continue;
1799 if((newmap = malloc(PAL_BYTES)) == NULL) {
1800 shutdown(1, "Error convert_map_to_palette for model: %s\n", model->name);
1802 // Create new colour map
1803 memcpy(newmap, model->palette, PAL_BYTES);
1804 oldmap = model->colourmap[c];
1805 for(i = 0; i < MAX_PAL_SIZE / 4; i++) {
1806 if(oldmap[i] == i)
1807 continue;
1808 p1 = newmap + i * pb;
1809 p2 = model->palette + oldmap[i] * pb;
1810 memcpy(p1, p2, pb);
1812 model->colourmap[c] = newmap;
1813 freeAndNull((void**) &oldmap);
1815 return 1;
1818 static int _load_palette16(unsigned char *palette, char *filename) {
1819 int handle, i;
1820 unsigned char tp[3];
1821 handle = openpackfile(filename, packfile);
1822 if(handle < 0)
1823 return 0;
1824 memset(palette, 0, MAX_PAL_SIZE / 2);
1825 for(i = 0; i < MAX_PAL_SIZE / 4; i++) {
1826 if(readpackfile(handle, tp, 3) != 3) {
1827 closepackfile(handle);
1828 return 0;
1830 ((unsigned short *) palette)[i] = colour16(tp[0], tp[1], tp[2]);
1832 closepackfile(handle);
1833 *(unsigned short *) palette = 0;
1835 return 1;
1839 static int _load_palette32(unsigned char *palette, char *filename) {
1840 int handle, i;
1841 unsigned *dp;
1842 unsigned char tpal[3];
1843 handle = openpackfile(filename, packfile);
1844 if(handle < 0)
1845 return 0;
1846 memset(palette, 0, MAX_PAL_SIZE);
1847 dp = (unsigned *) palette;
1848 for(i = 0; i < MAX_PAL_SIZE / 4; i++) {
1849 if(readpackfile(handle, tpal, 3) != 3) {
1850 closepackfile(handle);
1851 return 0;
1853 dp[i] = colour32(tpal[0], tpal[1], tpal[2]);
1856 closepackfile(handle);
1857 dp[0] = 0;
1859 return 1;
1862 //load a 256 colors' palette
1863 int load_palette(unsigned char *palette, char *filename) {
1864 int handle;
1865 if(screenformat == PIXEL_32)
1866 return _load_palette32(palette, filename);
1867 else if(screenformat == PIXEL_16)
1868 return _load_palette16(palette, filename);
1870 handle = openpackfile(filename, packfile);
1871 if(handle < 0)
1872 return 0;
1873 if(readpackfile(handle, palette, 768) != 768) {
1874 closepackfile(handle);
1875 return 0;
1877 closepackfile(handle);
1878 palette[0] = palette[1] = palette[2] = 0;
1880 return 1;
1883 // create blending tables for the palette
1884 int create_blending_tables(unsigned char *palette, unsigned char *tables[], int usemap[]) {
1885 int i;
1886 if(pixelformat != PIXEL_8)
1887 return 1;
1888 if(!palette || !tables)
1889 return 0;
1891 memset(tables, 0, MAX_BLENDINGS * sizeof(unsigned char *));
1892 for(i = 0; i < MAX_BLENDINGS; i++) {
1893 if(!usemap || usemap[i]) {
1894 tables[i] = (blending_table_functions[i]) (palette);
1895 if(!tables[i])
1896 return 0;
1900 return 1;
1904 //change system palette by index
1905 void change_system_palette(int palindex) {
1906 if(palindex < 0)
1907 palindex = 0;
1908 //if(current_palette == palindex ) return;
1911 if(!level || palindex == 0 || palindex > level->numpalettes) {
1912 current_palette = 0;
1913 if(screenformat == PIXEL_8) {
1914 palette_set_corrected(pal, savedata.gamma, savedata.gamma, savedata.gamma, savedata.brightness,
1915 savedata.brightness, savedata.brightness);
1916 set_blendtables(blendings); // set global blending tables
1918 } else if(level) {
1919 current_palette = palindex;
1920 if(screenformat == PIXEL_8) {
1921 palette_set_corrected(level->palettes[palindex - 1], savedata.gamma, savedata.gamma,
1922 savedata.gamma, savedata.brightness, savedata.brightness,
1923 savedata.brightness);
1924 set_blendtables(level->blendings[palindex - 1]);
1929 // Load colour 0-127 from data/pal.act
1930 void standard_palette(int immediate) {
1931 unsigned char *pp[MAX_PAL_SIZE] = { 0 };
1932 if(load_palette((unsigned char *) pp, "data/pal.act")) {
1933 memcpy(pal, pp, (PAL_BYTES) / 2);
1935 if(immediate) {
1936 change_system_palette(0);
1941 void unload_background() {
1942 int i;
1943 if(background)
1944 clearscreen(background);
1945 for(i = 0; i < MAX_BLENDINGS; i++) {
1946 freeAndNull((void**) &blendings[i]);
1951 int _makecolour(int r, int g, int b) {
1952 switch (screenformat) {
1953 case PIXEL_8:
1954 return palette_find(pal, r, g, b);
1955 case PIXEL_16:
1956 return colour16(r, g, b);
1957 case PIXEL_32:
1958 return colour32(r, g, b);
1960 return 0;
1963 // parses a color string in the format "R_G_B" or as a raw integer
1964 int parsecolor(const char *string) {
1965 int r, g, b;
1966 if(strchr(string, '_') != strrchr(string, '_')) { // 2 underscores; color is in "R_G_B" format
1967 r = atoi(string);
1968 g = atoi(strchr(string, '_') + 1);
1969 b = atoi(strrchr(string, '_') + 1);
1970 return _makecolour(r, g, b);
1971 } else
1972 return atoi(string); // raw integer
1975 // ltb 1-17-05 new function for lifebar colors
1976 void lifebar_colors() {
1977 char *filename = "data/lifebar.txt";
1978 char *buf;
1979 size_t size;
1980 int pos;
1981 ArgList arglist;
1982 char argbuf[MAX_ARG_LEN + 1] = "";
1983 char lowercase_buf[16];
1984 unsigned i;
1986 char *command;
1988 if(buffer_pakfile(filename, &buf, &size) != 1) {
1989 memset(&colors, 0, sizeof(colors));
1990 shadowalpha = BLEND_MULTIPLY + 1;
1991 return;
1994 typedef enum {
1995 LTC_BLACKBOX = 0,
1996 LTC_WHITEBOX,
1997 LTC_COLOR25,
1998 LTC_COLOR50,
1999 LTC_COLOR100,
2000 LTC_COLOR200,
2001 LTC_COLOR300,
2002 LTC_COLOR400,
2003 LTC_COLOR500,
2004 LTC_SHADOWCOLOR,
2005 LTC_COLORMAGIC,
2006 LTC_COLORMAGIC2,
2007 LTC_MAX
2008 } lifebar_txt_commands;
2010 static const char* lifebar_txt_commands_strings[] = {
2011 [LTC_BLACKBOX] = "blackbox",
2012 [LTC_WHITEBOX] = "whitebox",
2013 [LTC_COLOR25] = "color25",
2014 [LTC_COLOR50] = "color50",
2015 [LTC_COLOR100] = "color100",
2016 [LTC_COLOR200] = "color200",
2017 [LTC_COLOR300] = "color300",
2018 [LTC_COLOR400] = "color400",
2019 [LTC_COLOR500] = "color500",
2020 [LTC_SHADOWCOLOR] = "shadowcolor",
2021 [LTC_COLORMAGIC] = "colormagic",
2022 [LTC_COLORMAGIC2] = "colormagic2",
2025 static int* lifebar_txt_commands_destcolor[] = {
2026 [LTC_BLACKBOX] = &colors.black,
2027 [LTC_WHITEBOX] = &colors.white,
2028 [LTC_COLOR25] = &colors.red,
2029 [LTC_COLOR50] = &colors.yellow,
2030 [LTC_COLOR100] = &colors.green,
2031 [LTC_COLOR200] = &colors.blue,
2032 [LTC_COLOR300] = &colors.orange,
2033 [LTC_COLOR400] = &colors.pink,
2034 [LTC_COLOR500] = &colors.purple,
2035 [LTC_SHADOWCOLOR] = &colors.shadow,
2036 [LTC_COLORMAGIC] = &colors.magic,
2037 [LTC_COLORMAGIC2] = &colors.magic2,
2041 pos = 0;
2042 colorbars = 1;
2043 while(pos < size) {
2044 ParseArgs(&arglist, buf + pos, argbuf);
2045 command = GET_ARG(0);
2046 if(command && command[0]) {
2047 char_to_lower(lowercase_buf, command, sizeof(lowercase_buf));
2048 for(i = 0; i < LTC_MAX; i++) {
2049 if(!strcmp(lowercase_buf, lifebar_txt_commands_strings[i])) {
2050 *(lifebar_txt_commands_destcolor[i]) = _makecolour(GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3));
2051 break;
2054 if(i == LTC_MAX) {
2055 if(!strcmp(lowercase_buf, "shadowalpha")) //gfxshadow alpha
2056 shadowalpha = GET_INT_ARG(1);
2057 else
2058 printf("Warning: Unknown command in lifebar.txt: '%s'.\n", command);
2062 // Go to next line
2063 pos += getNewLineStart(buf + pos);
2065 freeAndNull((void**) &buf);
2068 // ltb 1-17-05 end new lifebar colors
2071 void init_colourtable() {
2072 color_tables.mp[0] = colors.magic2;
2073 color_tables.mp[1] = colors.magic;
2074 color_tables.mp[2] = colors.magic;
2075 color_tables.mp[3] = colors.magic;
2076 color_tables.mp[4] = colors.magic2;
2077 color_tables.mp[5] = colors.magic;
2078 color_tables.mp[6] = colors.magic2;
2079 color_tables.mp[7] = colors.magic;
2080 color_tables.mp[8] = colors.magic2;
2081 color_tables.mp[9] = colors.magic;
2082 color_tables.mp[10] = colors.magic2;
2084 color_tables.hp[0] = colors.purple;
2085 color_tables.hp[1] = colors.red;
2086 color_tables.hp[2] = colors.yellow;
2087 color_tables.hp[3] = colors.green;
2088 color_tables.hp[4] = colors.blue;
2089 color_tables.hp[5] = colors.orange;
2090 color_tables.hp[6] = colors.pink;
2091 color_tables.hp[7] = colors.purple;
2092 color_tables.hp[8] = colors.black;
2093 color_tables.hp[9] = colors.white;
2094 color_tables.hp[10] = colors.white;
2096 memcpy(color_tables.ld, color_tables.hp, 11 * sizeof(int));
2099 static void set_color_if_empty(int* color, s_rgb* rgb) {
2100 if(!*color) *color = _makecolour(rgb->r, rgb->g, rgb->b);
2103 void load_background(char *filename, int createtables) {
2104 //if(pixelformat!=PIXEL_8) createtables = 0;
2105 unsigned i;
2106 int *color_array = (int*) &colors;
2107 s_rgb* default_colors_array = (s_rgb*) &default_colors;
2108 unload_background();
2110 switch(pixelformat) {
2111 case PIXEL_8: case PIXEL_x8:
2112 if(!loadscreen(filename, packfile, pixelformat == PIXEL_8 ? pal : NULL, pixelformat, &background))
2113 shutdown(1, "Error loading background, file '%s'", filename);
2114 break;
2115 default:
2116 shutdown(1, "Error loading background, Unknown Pixel Format %d, file %s!\n", pixelformat, filename);
2119 if(createtables) {
2120 standard_palette(0);
2121 if(!create_blending_tables(pal, blendings, blendfx))
2122 shutdown(1, (char*) E_OUT_OF_MEMORY);
2125 lifebar_colors();
2126 for(i = 0; i < s_colors_itemcount; i++) {
2127 set_color_if_empty(&color_array[i], &default_colors_array[i]);
2129 init_colourtable();
2131 video_clearscreen();
2132 pal[0] = pal[1] = pal[2] = 0;
2133 //palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
2134 change_system_palette(0);
2137 void load_cached_background(char *filename, int createtables) {
2138 load_background(filename, createtables);
2141 void load_bglayer(char *filename, int index) {
2142 if(!level)
2143 return;
2145 // use screen for water effect for now, maybe get it to work with sprites at some point
2146 if(level->bglayers[index].watermode) {
2147 if(loadscreen(filename, packfile, NULL, pixelformat, &level->bglayers[index].screen)) {
2148 level->bglayers[index].height = level->bglayers[index].screen->height;
2149 level->bglayers[index].width = level->bglayers[index].screen->width;
2150 level->bglayers[index].type = bg_screen;
2152 } else if(level->bglayers[index].alpha > 0 || level->bglayers[index].transparency) {
2153 // assume sprites are faster than screen when transparency or alpha are specified
2154 level->bglayers[index].sprite =
2155 loadsprite2(filename, &(level->bglayers[index].width), &(level->bglayers[index].height));
2156 level->bglayers[index].type = bg_sprite;
2157 } else {
2158 // otherwise, a screen should be fine, especially in 8bit mode, it is super fast,
2159 // or, at least it is not slower than a sprite
2160 if(loadscreen(filename, packfile, NULL, pixelformat, &level->bglayers[index].screen)) {
2161 level->bglayers[index].height = level->bglayers[index].screen->height;
2162 level->bglayers[index].width = level->bglayers[index].screen->width;
2163 level->bglayers[index].type = bg_screen;
2167 if(level->bglayers[index].handle == NULL)
2168 shutdown(1, "Error loading file '%s'", filename);
2172 void load_fglayer(char *filename, int index) {
2173 if(!level)
2174 return;
2176 if(level->fglayers[index].alpha > 0 || level->fglayers[index].transparency) {
2177 // assume sprites are faster than screen when transparency or alpha are specified
2178 level->fglayers[index].sprite =
2179 loadsprite2(filename, &(level->fglayers[index].width), &(level->fglayers[index].height));
2180 level->fglayers[index].type = fg_sprite;
2181 } else {
2182 // otherwise, a screen should be fine, especially in 8bit mode, it is super fast,
2183 // or, at least it is not slower than a sprite
2184 if(loadscreen(filename, packfile, NULL, pixelformat, &level->fglayers[index].screen)) {
2185 level->fglayers[index].height = level->fglayers[index].screen->height;
2186 level->fglayers[index].width = level->fglayers[index].screen->width;
2187 level->fglayers[index].type = fg_screen;
2191 if(level->fglayers[index].handle == NULL)
2192 shutdown(1, "Error loading file '%s'", filename);
2196 void unload_texture() {
2197 freeAndNull((void**) &texture);
2200 void load_texture(char *filename) {
2201 unload_texture();
2202 texture = loadbitmap(filename, packfile, pixelformat);
2203 if(texture == NULL)
2204 shutdown(1, "Error loading file '%s'", filename);
2207 void freepanels() {
2208 int i;
2209 for(i = 0; i < MAX_PANELS; i++) {
2210 freeAndNull((void**) &panels[i].sprite_normal);
2211 freeAndNull((void**) &panels[i].sprite_neon);
2212 freeAndNull((void**) &panels[i].sprite_screen);
2213 freeAndNull((void**) &frontpanels[i]);
2215 panels_loaded = 0;
2216 frontpanels_loaded = 0;
2217 panel_width = 0;
2218 panel_height = 0;
2221 s_sprite *loadsprite2(char *filename, int *width, int *height) {
2222 size_t size;
2223 s_bitmap *bitmap = NULL;
2224 s_sprite *sprite = NULL;
2225 int clipl, clipr, clipt, clipb;
2227 bitmap = loadbitmap(filename, packfile, pixelformat);
2228 if(!bitmap)
2229 return NULL;
2230 if(width)
2231 *width = bitmap->width;
2232 if(height)
2233 *height = bitmap->height;
2234 clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
2235 size = fakey_encodesprite(bitmap);
2236 sprite = (s_sprite *) malloc(size);
2237 if(!sprite) {
2238 freebitmap(bitmap);
2239 return NULL;
2241 encodesprite(-clipl, -clipt, bitmap, sprite);
2242 freebitmap(bitmap);
2244 return sprite;
2247 s_sprite *loadpanel2(char *filename) {
2248 s_sprite *sprite;
2249 int w, h;
2251 if(NULL == (sprite = loadsprite2(filename, &w, &h)))
2252 return NULL;
2254 if(w > panel_width)
2255 panel_width = w;
2256 if(h > panel_height)
2257 panel_height = h;
2259 return sprite;
2262 int loadpanel(s_panel_filenames* filenames_s) {
2264 int i, loaded = 0;
2265 char** filenames = (char**) filenames_s;
2266 s_sprite** sprites = (s_sprite**) &panels[panels_loaded];
2268 if(panels_loaded >= MAX_PANELS)
2269 return 0;
2271 // check in case someone changes the order of s_panel
2272 assert(&sprites[1] == &panels[panels_loaded].sprite_neon);
2273 assert(&sprites[2] == &panels[panels_loaded].sprite_screen);
2275 for (i = 0; i < 3; i++) {
2276 if(stricmp(filenames[i], "none") != 0 && *filenames[i]) {
2277 sprites[i] = loadpanel2(filenames[i]);
2278 if(!sprites[i]) return 0;
2279 loaded = 1;
2280 if(i == 1) {
2281 //neon
2282 if(sprites[i]->palette) // under 24bit mode, copy the palette
2283 memcpy(neontable, sprites[i]->palette, PAL_BYTES);
2284 } else if (i == 2) {
2285 // screen
2286 if(!blendfx_is_set)
2287 blendfx[BLEND_SCREEN] = 1;
2292 if(!loaded)
2293 return 0; // Nothing was loaded!
2295 ++panels_loaded;
2297 return 1;
2300 int loadfrontpanel(char *filename) {
2302 size_t size;
2303 s_bitmap *bitmap = NULL;
2304 int clipl, clipr, clipt, clipb;
2307 if(frontpanels_loaded >= MAX_PANELS)
2308 return 0;
2309 bitmap = loadbitmap(filename, packfile, pixelformat);
2310 if(!bitmap)
2311 return 0;
2313 clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
2315 size = fakey_encodesprite(bitmap);
2316 frontpanels[frontpanels_loaded] = (s_sprite *) malloc(size);
2317 if(!frontpanels[frontpanels_loaded]) {
2318 freebitmap(bitmap);
2319 return 0;
2321 encodesprite(-clipl, -clipt, bitmap, frontpanels[frontpanels_loaded]);
2323 freebitmap(bitmap);
2324 ++frontpanels_loaded;
2326 return 1;
2329 // Added to conserve memory
2330 void resourceCleanUp() {
2331 freesprites();
2332 free_models();
2333 free_modelcache();
2334 load_special_sounds();
2335 load_script_setting();
2336 load_special_sprites();
2337 load_levelorder();
2338 load_models();
2341 void freesprites() {
2342 unsigned short i;
2343 s_sprite_list *head;
2344 for(i = 0; i <= sprites_loaded; i++) {
2345 if(sprite_list) {
2346 freeAndNull((void**) &sprite_list->sprite);
2347 freeAndNull((void**) &sprite_list->filename);
2348 head = sprite_list->next;
2349 free(sprite_list);
2350 sprite_list = head;
2353 freeAndNull((void**) &sprite_map);
2354 sprites_loaded = 0;
2357 // allocate enough members for sprite_map
2358 void prepare_sprite_map(size_t size) {
2359 if(sprite_map == NULL || size + 1 > sprite_map_max_items) {
2360 PDEBUG("%s %p\n", "prepare_sprite_map was", sprite_map);
2361 do {
2362 sprite_map_max_items += 256;
2364 while(size + 1 > sprite_map_max_items);
2365 sprite_map = realloc(sprite_map, sizeof(s_sprite_map) * sprite_map_max_items);
2366 if(sprite_map == NULL)
2367 shutdown(1, "Out Of Memory! Failed to create a new sprite_map\n");
2371 // Returns sprite index.
2372 // Does not return on error, as it would shut the program down.
2373 // UT:
2374 // bmpformat - In 24bit mode, a sprite can have a 24bit palette(e.g., panel),
2375 // so add this paramter to let sprite encoding function know.
2376 // Actually the sprite pixel encoding method is the same, but a
2377 // 24bit palettte sprite should have a palette allocated at the end of
2378 // pixel data, and the information is carried by the bitmap paramter.
2379 int loadsprite(char *filename, int ofsx, int ofsy, int bmpformat) {
2380 ptrdiff_t i, size;
2381 s_bitmap *bitmap = NULL;
2382 int clipl, clipr, clipt, clipb;
2383 s_sprite_list *curr = NULL, *head = NULL;
2385 for(i = 0; i < sprites_loaded; i++) {
2386 if(sprite_map != NULL) {
2387 if(stricmp(sprite_map[i].filename, filename) == 0) {
2388 if(sprite_map[i].ofsx == ofsx && sprite_map[i].ofsy == ofsy)
2389 return i;
2390 else {
2391 bitmap = loadbitmap(filename, packfile, bmpformat);
2392 if(bitmap == NULL)
2393 shutdown(1, "Unable to load file '%s'\n", filename);
2394 clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
2395 prepare_sprite_map(sprites_loaded + 1);
2396 sprite_map[sprites_loaded].filename = sprite_map[i].filename;
2397 sprite_map[sprites_loaded].sprite = sprite_map[i].sprite;
2398 sprite_map[sprites_loaded].ofsx = ofsx;
2399 sprite_map[sprites_loaded].ofsy = ofsy;
2400 sprite_map[sprites_loaded].centerx = ofsx - clipl;
2401 sprite_map[sprites_loaded].centery = ofsy - clipt;
2402 freebitmap(bitmap);
2403 ++sprites_loaded;
2404 return sprites_loaded - 1;
2410 bitmap = loadbitmap(filename, packfile, bmpformat);
2411 if(bitmap == NULL)
2412 shutdown(1, "Unable to load file '%s'\n", filename);
2414 clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
2416 curr = malloc(sizeof(s_sprite_list));
2417 if(!curr) goto eoom;
2418 curr->filename = strdup(filename);
2419 size = fakey_encodesprite(bitmap);
2420 curr->sprite = malloc(size);
2422 if(!curr->sprite || !curr->filename) {
2423 eoom:
2424 freebitmap(bitmap);
2425 shutdown(1, (char*) E_OUT_OF_MEMORY);
2427 encodesprite(ofsx - clipl, ofsy - clipt, bitmap, curr->sprite);
2428 if(sprite_list == NULL) {
2429 sprite_list = curr;
2430 sprite_list->next = NULL;
2431 } else {
2432 head = sprite_list;
2433 sprite_list = curr;
2434 sprite_list->next = head;
2436 prepare_sprite_map(sprites_loaded + 1);
2437 sprite_map[sprites_loaded].filename = sprite_list->filename;
2438 sprite_map[sprites_loaded].sprite = sprite_list->sprite;
2439 sprite_map[sprites_loaded].ofsx = ofsx;
2440 sprite_map[sprites_loaded].ofsy = ofsy;
2441 sprite_map[sprites_loaded].centerx = ofsx - clipl;
2442 sprite_map[sprites_loaded].centery = ofsy - clipt;
2443 freebitmap(bitmap);
2444 ++sprites_loaded;
2445 return sprites_loaded - 1;
2448 void load_special_sprites() {
2449 unsigned i;
2450 for(i = 0; i < special_sprites_init_itemcount; i++) {
2451 if(testpackfile(special_sprites_init[i].fn, packfile) >= 0) {
2452 *special_sprites_init[i].target =
2453 loadsprite(special_sprites_init[i].fn,
2454 special_sprites_init[i].ofsx,
2455 special_sprites_init[i].ofsy,
2456 pixelformat);
2457 } else
2458 *special_sprites_init[i].target = -1;
2460 if(timeicon_path[0])
2461 timeicon = loadsprite(timeicon_path, 0, 0, pixelformat);
2462 if(bgicon_path[0])
2463 bgicon = loadsprite(bgicon_path, 0, 0, pixelformat);
2464 if(olicon_path[0])
2465 olicon = loadsprite(olicon_path, 0, 0, pixelformat);
2468 void unload_all_fonts() {
2469 int i;
2470 for(i = 0; i < 8; i++) {
2471 font_unload(i);
2474 void load_all_fonts() {
2475 unsigned i;
2476 for(i = 0; i < font_init_itemcount; i++) {
2477 if(font_init[i].obligatory) {
2478 if(!font_load(i, font_init[i].path, packfile, fontmonospace[i]))
2479 shutdown(1, "Unable to load font #%d!\n", i);
2480 } else {
2481 if(testpackfile(font_init[i].path, packfile) >= 0)
2482 font_load(i, font_init[i].path, packfile, fontmonospace[i]);
2487 //stringswitch_gen add menutxt_cmd "disablekey"
2488 //stringswitch_gen add menutxt_cmd "renamekey"
2489 //stringswitch_gen add menutxt_cmd "fontmonospace"
2490 #include "stringswitch_impl_menutxt_cmd.c"
2492 void load_menu_txt() {
2493 char *filename = "data/menu.txt";
2494 char lowercase_buf[16];
2495 int pos;
2496 char *buf, *command;
2497 size_t size;
2498 ArgList arglist;
2499 char argbuf[MAX_ARG_LEN + 1] = "";
2500 unsigned i, line = 1;
2502 if(buffer_pakfile(filename, &buf, &size)) {
2503 // Now interpret the contents of buf line by line
2504 pos = 0;
2505 while(pos < size) {
2506 ParseArgs(&arglist, buf + pos, argbuf);
2507 command = GET_ARG(0);
2508 if(command[0]) {
2509 lc(command, GET_ARG_LEN(0));
2510 stringswitch_d(menutxt_cmd, command) {
2511 stringcase(menutxt_cmd, disablekey):
2512 char_to_lower(lowercase_buf, GET_ARG(1), sizeof(lowercase_buf));
2513 for(i = 0; i < CB_MAX; i++) {
2514 if(!strcmp(lowercase_buf, ((char**)&config_button_names)[i])) {
2515 disabledkey[i] = 1;
2516 break;
2519 break;
2520 stringcase(menutxt_cmd, renamekey):
2521 char_to_lower(lowercase_buf, GET_ARG(1), sizeof(lowercase_buf));
2522 for(i = 0; i < CB_MAX; i++) {
2523 if(!strcmp(lowercase_buf, ((char**)&config_button_names)[i])) {
2524 strncpy(custom_button_names[i], GET_ARG(2), 16);
2525 ((char**)&buttonnames)[i] = custom_button_names[i];
2526 break;
2529 stringcase(menutxt_cmd, fontmonospace):
2530 for(i = 0; i < 8; i++)
2531 fontmonospace[i] = GET_INT_ARG(i+1);
2532 break;
2533 default:
2534 if(command && command[0])
2535 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
2539 // Go to next line
2540 pos += getNewLineStart(buf + pos);
2542 freeAndNull((void**) &buf);
2546 static const s_samples_strings samples_special_filenames = {
2547 .go = "data/sounds/go.wav",
2548 .beat = "data/sounds/beat1.wav",
2549 .block = "data/sounds/block.wav",
2550 .indirect = "data/sounds/indirect.wav",
2551 .get = "data/sounds/get.wav",
2552 .get2 = "data/sounds/money.wav",
2553 .fall = "data/sounds/fall.wav",
2554 .jump = "data/sounds/jump.wav",
2555 .punch = "data/sounds/punch.wav",
2556 .oneup = "data/sounds/1up.wav",
2557 .timeover = "data/sounds/timeover.wav",
2558 .beep = "data/sounds/beep.wav",
2559 .beep2 = "data/sounds/beep2.wav",
2560 .bike = "data/sounds/bike.wav",
2563 int load_special_sounds() {
2564 unsigned i;
2565 int *samples_array = (int*) &samples;
2566 char** samples_filenames_array = (char**) &samples_special_filenames;
2567 sound_unload_all_samples();
2568 for(i = 0; i < s_samples_itemcount; i++) {
2569 samples_array[i] = sound_load_sample(samples_filenames_array[i], packfile, 0);
2571 for(i = 0; i < s_samples_itemcount; i++) {
2572 if(samples_array[i] < 0) return 0;
2574 return 1;
2577 // Use by player select menus
2578 s_model *nextplayermodel(s_model * current) {
2579 int i;
2580 int curindex = -1;
2581 int loops;
2582 if(current) {
2583 // Find index of current player model
2584 for(i = 0; i < models_cached; i++) {
2585 if(model_cache[i].model == current) {
2586 curindex = i;
2587 break;
2591 // Find next player model (first one after current index)
2592 for(i = curindex + 1, loops = 0; loops < models_cached; i++, loops++) {
2593 if(i >= models_cached)
2594 i = 0;
2595 if(model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER &&
2596 (allow_secret_chars || !model_cache[i].model->secret) && model_cache[i].selectable) {
2597 return model_cache[i].model;
2600 shutdown(1, "Fatal: can't find any player models!");
2601 return NULL;
2604 // Use by player select menus
2605 s_model *prevplayermodel(s_model * current) {
2606 int i;
2607 int curindex = -1;
2608 int loops;
2609 if(current) {
2610 // Find index of current player model
2611 for(i = 0; i < models_cached; i++) {
2612 if(model_cache[i].model == current) {
2613 curindex = i;
2614 break;
2618 // Find next player model (first one after current index)
2619 for(i = curindex - 1, loops = 0; loops < models_cached; i--, loops++) {
2620 if(i < 0)
2621 i = models_cached - 1;
2622 if(model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER &&
2623 (allow_secret_chars || !model_cache[i].model->secret) && model_cache[i].selectable) {
2624 return model_cache[i].model;
2627 shutdown(1, "Fatal: can't find any player models!");
2628 return NULL;
2631 // Reset All Player Models to on/off for Select Screen.
2632 static void reset_playable_list(char which) {
2633 int i;
2634 for(i = 0; i < models_cached; i++) {
2635 if(model_cache[i].model && model_cache[i].model->type == TYPE_PLAYER) {
2636 model_cache[i].selectable = which;
2641 // Specify which Player Models are allowable for selecting
2642 static void load_playable_list(char *buf) {
2643 int i, index;
2644 char *value;
2645 s_model *playermodels = NULL;
2646 ArgList arglist;
2647 char argbuf[MAX_ARG_LEN + 1] = "";
2649 reset_playable_list(0);
2650 ParseArgs(&arglist, buf, argbuf);
2652 for(i = 1; (value = GET_ARG(i))[0]; i++) {
2653 playermodels = findmodel(value);
2654 if(playermodels == NULL)
2655 shutdown(1, "Player model '%s' is not loaded.\n", value);
2656 index = get_cached_model_index(playermodels->name);
2657 if(index == -1)
2658 shutdown(1, "Player model '%s' is not cached.\n", value);
2659 model_cache[index].selectable = 1;
2663 void alloc_frames(s_anim * anim, int fcount) {
2664 anim->sprite = malloc(fcount * sizeof(anim->sprite));
2665 anim->delay = malloc(fcount * sizeof(anim->delay));
2666 anim->vulnerable = malloc(fcount * sizeof(anim->vulnerable));
2667 memset(anim->sprite, 0, fcount * sizeof(anim->sprite));
2668 memset(anim->delay, 0, fcount * sizeof(anim->delay));
2669 memset(anim->vulnerable, 0, fcount * sizeof(anim->vulnerable));
2672 void free_frames(s_anim * anim) {
2673 int i;
2674 freeAndNull((void**) &anim->idle);
2675 freeAndNull((void**) &anim->seta);
2676 freeAndNull((void**) &anim->move);
2677 freeAndNull((void**) &anim->movez);
2678 freeAndNull((void**) &anim->movea);
2679 freeAndNull((void**) &anim->delay);
2680 freeAndNull((void**) &anim->sprite);
2681 freeAndNull((void**) &anim->platform);
2682 freeAndNull((void**) &anim->vulnerable);
2683 freeAndNull((void**) &anim->bbox_coords);
2684 freeAndNull((void**) &anim->shadow);
2685 freeAndNull((void**) &anim->shadow_coords);
2686 freeAndNull((void**) &anim->soundtoplay);
2687 if(anim->attacks) {
2688 for(i = 0; i < anim->numframes; i++)
2689 freeAndNull((void**) &anim->attacks[i]);
2690 freeAndNull((void**) &anim->attacks);
2692 if(anim->drawmethods) {
2693 for(i = 0; i < anim->numframes; i++)
2694 freeAndNull((void**) &anim->drawmethods[i]);
2695 freeAndNull((void**) &anim->drawmethods);
2699 s_anim_list *anim_list_delete(s_anim_list * list, int index) {
2700 if(list == NULL)
2701 return NULL;
2702 if(list->anim->model_index == index) {
2703 s_anim_list *next;
2704 next = list->next;
2705 free_anim(list->anim);
2706 free(list);
2707 return next;
2709 list->next = anim_list_delete(list->next, index);
2710 return list;
2713 void free_anim(s_anim * anim) {
2714 if(!anim)
2715 return;
2716 free_frames(anim);
2717 freeAndNull((void**) &anim->weaponframe);
2718 freeAndNull((void**) &anim->spawnframe);
2719 freeAndNull((void**) &anim->summonframe);
2720 freeAndNull((void**) &anim);
2723 int hasFreetype(s_model * m, ModelFreetype t) {
2724 assert(m);
2725 return (m->freetypes & t) == t;
2728 void addFreeType(s_model * m, ModelFreetype t) {
2729 assert(m);
2730 m->freetypes |= t;
2733 // Unload single model from memory
2734 int free_model(s_model * model) {
2735 int i;
2736 if(!model)
2737 return 0;
2738 printf("Unload '%s'\n", model->name);
2740 if(hasFreetype(model, MF_ANIMLIST))
2741 for(i = 0; i < dyn_anim_custom_maxvalues.max_animations; i++)
2742 anim_list = anim_list_delete(anim_list, model->index);
2744 if(hasFreetype(model, MF_COLOURMAP))
2745 for(i = 0; i < MAX_COLOUR_MAPS; i++)
2746 freeAndNull((void**) &model->colourmap[i]);
2748 if(hasFreetype(model, MF_PALETTE) && model->palette)
2749 freeAndNull((void**) &model->palette);
2751 if(hasFreetype(model, MF_WEAPONS) && model->weapon && model->ownweapons)
2752 freeAndNull((void**) &model->weapon);
2754 if(hasFreetype(model, MF_BRANCH) && model->branch)
2755 freeAndNull((void**) &model->branch);
2757 if(hasFreetype(model, MF_ANIMATION) && model->animation)
2758 freeAndNull((void**) &model->animation);
2760 if(hasFreetype(model, MF_DEF_FACTORS) && model->defense_factors)
2761 freeAndNull((void**) &model->defense_factors);
2763 if(hasFreetype(model, MF_DEF_PAIN) && model->defense_pain)
2764 freeAndNull((void**) &model->defense_pain);
2766 if(hasFreetype(model, MF_DEF_KNOCKDOWN) && model->defense_knockdown)
2767 freeAndNull((void**) &model->defense_knockdown);
2769 if(hasFreetype(model, MF_DEF_BLOCKPOWER) && model->defense_blockpower)
2770 freeAndNull((void**) &model->defense_blockpower);
2772 if(hasFreetype(model, MF_DEF_BLOCKTRESHOLD) && model->defense_blockthreshold)
2773 freeAndNull((void**) &model->defense_blockthreshold);
2775 if(hasFreetype(model, MF_DEF_BLOCKRATIO) && model->defense_blockratio)
2776 freeAndNull((void**) &model->defense_blockratio);
2778 if(hasFreetype(model, MF_DEF_BLOCKTYPE) && model->defense_blocktype)
2779 freeAndNull((void**) &model->defense_blocktype);
2781 if(hasFreetype(model, MF_OFF_FACTORS) && model->offense_factors)
2782 freeAndNull((void**) &model->offense_factors);
2784 if(hasFreetype(model, MF_SPECIAL) && model->special)
2785 freeAndNull((void**) &model->special);
2787 if(hasFreetype(model, MF_SMARTBOMB) && model->smartbomb)
2788 freeAndNull((void**) &model->smartbomb);
2790 if(hasFreetype(model, MF_SCRIPTS)) {
2791 clear_all_scripts(&model->scripts, 2);
2792 free_all_scripts(&model->scripts);
2795 deleteModel(model->name);
2797 return models_loaded--;
2800 void freeAnims(void) {
2801 unsigned i;
2802 int** dyn_anims_arr = (int**) &dyn_anims;
2803 for (i = 0; i < dyn_anim_itemcount; i++) {
2804 freeAndNull((void**) &dyn_anims_arr[i]);
2808 // Unload all models and animations memory
2809 void free_models(void) {
2810 s_model *temp;
2812 while((temp = getFirstModel()))
2813 free_model(temp);
2815 // free animation ids
2816 freeAnims();
2820 s_anim *alloc_anim() {
2821 s_anim_list *curr = NULL, *head = NULL;
2822 curr = malloc(sizeof(s_anim_list));
2823 if(!curr) return NULL;
2824 curr->anim = calloc(1, sizeof(s_anim));
2825 if(!curr->anim) {
2826 free(curr);
2827 return NULL;
2829 if(anim_list == NULL) {
2830 anim_list = curr;
2831 anim_list->next = NULL;
2832 } else {
2833 head = anim_list;
2834 anim_list = curr;
2835 anim_list->next = head;
2837 ++anims_loaded;
2838 return anim_list->anim;
2842 int addframe(s_anim * a, int spriteindex, int framecount, short delay, unsigned char idle,
2843 short *bbox, s_attack * attack, short move, short movez,
2844 short movea, short seta, float *platform, int frameshadow, short *shadow_coords, int soundtoplay,
2845 s_drawmethod * drawmethod) {
2846 ptrdiff_t currentframe;
2847 if(framecount > 0)
2848 alloc_frames(a, framecount);
2849 else
2850 framecount = -framecount; // for alloc method, use a negative value
2852 currentframe = a->numframes;
2853 ++a->numframes;
2855 a->sprite[currentframe] = spriteindex;
2856 a->delay[currentframe] = delay * GAME_SPEED / 100;
2858 if((bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
2859 if(!a->bbox_coords) {
2860 a->bbox_coords = calloc(framecount, sizeof(*a->bbox_coords));
2862 memcpy(a->bbox_coords[currentframe], bbox, sizeof(*a->bbox_coords));
2863 a->vulnerable[currentframe] = 1;
2865 if((attack->attack_coords[2] - attack->attack_coords[0]) &&
2866 (attack->attack_coords[3] - attack->attack_coords[1])) {
2867 if(!a->attacks) {
2868 a->attacks = calloc(framecount, sizeof(s_attack *));
2870 a->attacks[currentframe] = malloc(sizeof(s_attack));
2871 memcpy(a->attacks[currentframe], attack, sizeof(s_attack));
2873 if(drawmethod->flag) {
2874 if(!a->drawmethods) {
2875 a->drawmethods = calloc(framecount, sizeof(s_drawmethod *));
2877 setDrawMethod(a, currentframe, malloc(sizeof(s_drawmethod)));
2878 //a->drawmethods[currenframe] = malloc(sizeof(s_drawmethod));
2879 memcpy(getDrawMethod(a, currentframe), drawmethod, sizeof(s_drawmethod));
2880 //memcpy(a->drawmethods[currentframe], drawmethod, sizeof(s_drawmethod));
2882 if(idle && !a->idle) {
2883 a->idle = calloc(framecount, sizeof(*a->idle));
2885 if(a->idle)
2886 a->idle[currentframe] = idle;
2887 if(move && !a->move) {
2888 a->move = calloc(framecount, sizeof(*a->move));
2890 if(a->move)
2891 a->move[currentframe] = move;
2892 if(movez && !a->movez) {
2893 a->movez = calloc(framecount, sizeof(*a->movez));
2895 if(a->movez)
2896 a->movez[currentframe] = movez; // Move command for the "z" axis
2897 if(movea && !a->movea) {
2898 a->movea = calloc(framecount, sizeof(*a->movea));
2900 if(a->movea)
2901 a->movea[currentframe] = movea; // Move command for moving along the "a" axis
2902 if(seta >= 0 && !a->seta) {
2903 a->seta = malloc(framecount * sizeof(*a->seta));
2904 memset(a->seta, -1, framecount * sizeof(*a->seta)); //default to -1
2906 if(a->seta)
2907 a->seta[currentframe] = seta; // Sets the "a" for the character on a frame/frame basis
2908 if(frameshadow >= 0 && !a->shadow) {
2909 a->shadow = malloc(framecount * sizeof(*a->shadow));
2910 memset(a->shadow, -1, framecount * sizeof(*a->shadow)); //default to -1
2912 if(a->shadow)
2913 a->shadow[currentframe] = frameshadow; // shadow index for each frame
2914 if(shadow_coords[0] || shadow_coords[1]) {
2915 if(!a->shadow_coords) {
2916 a->shadow_coords = calloc(framecount, sizeof(*a->shadow_coords));
2918 memcpy(a->shadow_coords[currentframe], shadow_coords, sizeof(*a->shadow_coords));
2920 if(platform[7]) //height
2922 if(!a->platform) {
2923 a->platform = calloc(framecount, sizeof(*a->platform));
2925 memcpy(a->platform[currentframe], platform, sizeof(*a->platform)); // Used so entity can be landed on
2927 if(soundtoplay >= 0) {
2928 if(!a->soundtoplay) {
2929 a->soundtoplay = malloc(framecount * sizeof(*a->soundtoplay));
2930 memset(a->soundtoplay, -1, framecount * sizeof(*a->soundtoplay)); // default to -1
2932 a->soundtoplay[currentframe] = soundtoplay;
2935 return a->numframes;
2939 // ok this func only seems to overwrite the name which was assigned from models.txt with the one
2940 // in the models own text file.
2941 // it does so in the cache.
2942 void _peek_model_name(int index) {
2943 size_t size = 0;
2944 ptrdiff_t pos = 0;
2945 char *buf = NULL;
2946 char *command, *value;
2947 ArgList arglist;
2948 char argbuf[MAX_ARG_LEN + 1] = "";
2949 modelCommands cmd;
2951 if(buffer_pakfile(model_cache[index].path, &buf, &size) != 1)
2952 return;
2954 while(pos < size) {
2955 ParseArgs(&arglist, buf + pos, argbuf);
2956 command = GET_ARG(0);
2958 if(command && command[0]) {
2959 cmd = getModelCommand(modelcmdlist, command);
2960 if(cmd == CMD_MODEL_NAME) {
2961 value = GET_ARG(1);
2962 freeAndNull((void**) &model_cache[index].name);
2963 model_cache[index].name = strdup(value);
2964 break;
2967 pos += getNewLineStart(buf + pos);
2969 freeAndNull((void**) &buf);
2972 void prepare_cache_map(size_t size) {
2973 if(model_cache == NULL || size + 1 > cache_map_max_items) {
2974 PDEBUG("%s %p\n", "prepare_cache_map was", model_cache);
2975 do {
2976 cache_map_max_items += 128;
2978 while(size + 1 > cache_map_max_items);
2980 model_cache = realloc(model_cache, sizeof(s_modelcache) * cache_map_max_items);
2981 if(model_cache == NULL)
2982 shutdown(1, "Out Of Memory! Failed to create a new cache_map\n");
2986 void cache_model(char *name, char *path, int flag) {
2987 printf("Cacheing '%s' from %s\n", name, path);
2988 prepare_cache_map(models_cached + 1);
2989 memset(&model_cache[models_cached], 0, sizeof(s_modelcache));
2991 model_cache[models_cached].name = strdup(name);
2992 model_cache[models_cached].path = strdup(path);
2994 model_cache[models_cached].loadflag = flag;
2996 _peek_model_name(models_cached);
2997 ++models_cached;
3001 void free_modelcache() {
3002 if(model_cache != NULL) {
3003 while(models_cached) {
3004 --models_cached;
3005 freeAndNull((void**) &model_cache[models_cached].name);
3006 freeAndNull((void**) &model_cache[models_cached].path);
3008 freeAndNull((void**) &model_cache);
3013 int get_cached_model_index(char *name) {
3014 int i;
3015 for(i = 0; i < models_cached; i++) {
3016 if(stricmp(name, model_cache[i].name) == 0)
3017 return i;
3019 return -1;
3022 char *get_cached_model_path(char *name) {
3023 int i;
3024 for(i = 0; i < models_cached; i++) {
3025 if(stricmp(name, model_cache[i].name) == 0)
3026 return model_cache[i].path;
3028 return NULL;
3031 static void _readbarstatus(char *, s_barstatus *);
3033 //alloc a new model, and everything thats required,
3034 //set all values to defaults
3035 s_model *init_model(int cacheindex, int unload) {
3036 //to free: newchar, newchar->offense_factors, newchar->special, newchar->animation - OK
3037 int i;
3039 s_model *newchar = calloc(1, sizeof(s_model));
3040 if(!newchar)
3041 shutdown(1, (char *) E_OUT_OF_MEMORY);
3042 newchar->name = model_cache[cacheindex].name; // well give it a name for sort method
3043 newchar->index = cacheindex;
3044 newchar->isSubclassed = 0;
3045 newchar->freetypes = MF_ALL;
3047 newchar->defense_factors = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3048 newchar->defense_pain = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3049 newchar->defense_knockdown = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3050 newchar->defense_blockpower = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3051 newchar->defense_blockthreshold = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3052 newchar->defense_blockratio = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3053 newchar->defense_blocktype = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3054 newchar->offense_factors = (float *) calloc(sizeof(float), (dyn_anim_custom_maxvalues.max_attack_types + 1));
3056 newchar->special = calloc(sizeof(*newchar->special), dyn_anim_custom_maxvalues.max_freespecials);
3057 if(!newchar->special)
3058 shutdown(1, (char *) E_OUT_OF_MEMORY);
3060 alloc_all_scripts(&newchar->scripts);
3062 newchar->unload = unload;
3063 newchar->jumpspeed = -1;
3064 newchar->jumpheight = 4; // 28-12-2004 Set default jump height to 4, if not specified
3065 newchar->runjumpheight = 4; // Default jump height if a player is running
3066 newchar->runjumpdist = 1; // Default jump distane if a player is running
3067 newchar->grabdistance = 36; // 30-12-2004 Default grabdistance is same as originally set
3068 newchar->throwdamage = 21; // default throw damage
3069 newchar->icon = -1;
3070 newchar->icondie = -1;
3071 newchar->iconpain = -1;
3072 newchar->iconget = -1;
3073 newchar->iconw = -1; // No weapon icon set yet
3074 newchar->diesound = -1;
3075 newchar->nolife = 0; // default show life = 1 (yes)
3076 newchar->remove = 1; // Flag set to weapons are removed upon hitting an opponent
3077 newchar->throwdist = 2.5;
3078 newchar->counter = 3; // Default 3 times to drop a weapon / projectile
3079 newchar->aimove = -1;
3080 newchar->aiattack = -1;
3081 newchar->throwframewait = -1; // makes sure throw animations run normally unless throwfram is specified, added by kbandressen 10/20/06
3082 newchar->path = model_cache[cacheindex].path; // Record path, so script can get it without looping the whole model collection.
3084 for(i = 0; i < 3; i++)
3085 newchar->iconmp[i] = -1; // No magicbar icon set yet
3087 // Default Attack1 in chain must be referenced if not used.
3088 for(i = 0; i < MAX_ATCHAIN; i++)
3089 newchar->atchain[i] = 1;
3090 newchar->chainlength = 1;
3092 if(magic_type == 1)
3093 newchar->mprate = 1;
3094 else
3095 newchar->mprate = 2;
3096 newchar->chargerate = newchar->guardrate = 2;
3097 newchar->risetime[0] = -1;
3098 newchar->sleepwait = 1000;
3099 newchar->jugglepoints[0] = newchar->jugglepoints[1] = 0;
3100 newchar->guardpoints[0] = newchar->guardpoints[1] = 0;
3101 newchar->mpswitch = -1; // switch between reduce mp or gain mp for mpstabletype 4
3102 newchar->weaploss[0] = -1;
3103 newchar->weaploss[1] = -1;
3104 newchar->lifespan = (float) 0xFFFFFFFF;
3105 newchar->summonkill = 1;
3106 newchar->candamage = -1;
3107 newchar->hostile = -1;
3108 newchar->projectilehit = -1;
3109 newchar->subject_to_wall = -1;
3110 newchar->subject_to_platform = -1;
3111 newchar->subject_to_obstacle = -1;
3112 newchar->subject_to_hole = -1;
3113 newchar->subject_to_gravity = -1;
3114 newchar->subject_to_screen = -1;
3115 newchar->subject_to_minz = -1;
3116 newchar->subject_to_maxz = -1;
3117 newchar->no_adjust_base = -1;
3118 newchar->pshotno = -1;
3119 newchar->project = -1;
3120 newchar->dust[0] = -1;
3121 newchar->dust[1] = -1;
3122 newchar->dust[2] = -1;
3123 newchar->bomb = -1;
3124 newchar->star = -1;
3125 newchar->knife = -1;
3127 newchar->animation = (s_anim **) calloc(sizeof(s_anim *), dyn_anim_custom_maxvalues.max_animations);
3128 if(!newchar->animation)
3129 shutdown(1, (char *) E_OUT_OF_MEMORY);
3131 // default string value, only by reference
3132 newchar->rider = get_cached_model_index("K'");
3133 newchar->flash = newchar->bflash = get_cached_model_index("flash");
3135 //Default offense/defense values.
3136 for(i = 0; i < dyn_anim_custom_maxvalues.max_attack_types; i++) {
3137 newchar->offense_factors[i] = 1;
3138 newchar->defense_factors[i] = 1;
3139 newchar->defense_knockdown[i] = 1;
3142 for(i = 0; i < 3; i++) {
3143 newchar->sight[i * 2] = -9999;
3144 newchar->sight[i * 2 + 1] = 9999;
3147 newchar->offense_factors[ATK_BLAST] = 1;
3148 newchar->defense_factors[ATK_BLAST] = 1;
3149 newchar->defense_knockdown[ATK_BLAST] = 1;
3150 newchar->offense_factors[ATK_BURN] = 1;
3151 newchar->defense_factors[ATK_BURN] = 1;
3152 newchar->defense_knockdown[ATK_BURN] = 1;
3153 newchar->offense_factors[ATK_FREEZE] = 1;
3154 newchar->defense_factors[ATK_FREEZE] = 1;
3155 newchar->defense_knockdown[ATK_FREEZE] = 1;
3156 newchar->offense_factors[ATK_SHOCK] = 1;
3157 newchar->defense_factors[ATK_SHOCK] = 1;
3158 newchar->defense_knockdown[ATK_SHOCK] = 1;
3159 newchar->offense_factors[ATK_STEAL] = 1;
3160 newchar->defense_factors[ATK_STEAL] = 1;
3161 newchar->defense_knockdown[ATK_STEAL] = 1;
3163 return newchar;
3166 void update_model_loadflag(s_model * model, char unload) {
3167 model->unload = unload;
3170 void lcmHandleCommandName(ArgList * arglist, s_model * newchar, int cacheindex) {
3171 char *value = GET_ARGP(1);
3172 s_model *tempmodel;
3173 if((tempmodel = findmodel(value)) && tempmodel != newchar)
3174 shutdown(1, "Duplicate model name '%s'", value);
3175 /*if((tempmodel=find_model(value))) {
3176 return tempmodel;
3177 } */
3178 model_cache[cacheindex].model = newchar;
3179 newchar->name = model_cache[cacheindex].name;
3180 if(stricmp(newchar->name, "steam") == 0) {
3181 newchar->alpha = 1;
3185 //stringswitch_gen add lcm_cmdtype "none"
3186 //stringswitch_gen add lcm_cmdtype "player"
3187 //stringswitch_gen add lcm_cmdtype "enemy"
3188 //stringswitch_gen add lcm_cmdtype "item"
3189 //stringswitch_gen add lcm_cmdtype "obstacle"
3190 //stringswitch_gen add lcm_cmdtype "steamer"
3191 //stringswitch_gen add lcm_cmdtype "pshot"
3192 //stringswitch_gen add lcm_cmdtype "trap"
3193 //stringswitch_gen add lcm_cmdtype "text"
3194 //stringswitch_gen add lcm_cmdtype "endlevel"
3195 //stringswitch_gen add lcm_cmdtype "npc"
3196 //stringswitch_gen add lcm_cmdtype "panel"
3198 #include "stringswitch_impl_lcm_cmdtype.c"
3200 void lcmHandleCommandType(ArgList * arglist, s_model * newchar, char *filename) {
3201 char *value = GET_ARGP(1);
3202 int i;
3203 lc(value, GET_ARGP_LEN(1));
3204 stringswitch_d(lcm_cmdtype, value) {
3205 stringcase(lcm_cmdtype, none):
3206 newchar->type = TYPE_NONE;
3207 break;
3208 stringcase(lcm_cmdtype, player):
3209 newchar->type = TYPE_PLAYER;
3210 newchar->nopassiveblock = 1;
3211 for(i = 0; i < MAX_ATCHAIN; i++) {
3212 if(i < 2 || i > 3)
3213 newchar->atchain[i] = 1;
3214 else
3215 newchar->atchain[i] = i;
3217 newchar->chainlength = 4;
3218 newchar->bounce = 1;
3219 newchar->subject_to_wall = 1;
3220 newchar->subject_to_platform = 1;
3221 newchar->subject_to_obstacle = 1;
3222 newchar->subject_to_hole = 1;
3223 newchar->subject_to_gravity = 1;
3224 newchar->subject_to_screen = 1;
3225 newchar->subject_to_minz = 1;
3226 newchar->subject_to_maxz = 1;
3227 newchar->no_adjust_base = 0;
3228 break;
3229 stringcase(lcm_cmdtype, enemy):
3230 newchar->type = TYPE_ENEMY;
3231 newchar->bounce = 1;
3232 newchar->subject_to_wall = 1;
3233 newchar->subject_to_platform = 1;
3234 newchar->subject_to_hole = 1;
3235 newchar->subject_to_obstacle = 1;
3236 newchar->subject_to_gravity = 1;
3237 newchar->subject_to_minz = 1;
3238 newchar->subject_to_maxz = 1;
3239 newchar->no_adjust_base = 0;
3240 break;
3241 stringcase(lcm_cmdtype, item):
3242 newchar->type = TYPE_ITEM;
3243 newchar->subject_to_wall = 1;
3244 newchar->subject_to_platform = 1;
3245 newchar->subject_to_hole = 1;
3246 newchar->subject_to_obstacle = 1;
3247 newchar->subject_to_gravity = 1;
3248 newchar->subject_to_minz = 1;
3249 newchar->subject_to_maxz = 1;
3250 newchar->no_adjust_base = 0;
3251 break;
3252 stringcase(lcm_cmdtype, obstacle):
3253 newchar->type = TYPE_OBSTACLE;
3254 newchar->subject_to_wall = 1;
3255 newchar->subject_to_platform = 1;
3256 newchar->subject_to_hole = 1;
3257 newchar->subject_to_gravity = 1;
3258 newchar->subject_to_minz = 1;
3259 newchar->subject_to_maxz = 1;
3260 newchar->no_adjust_base = 0;
3261 break;
3262 stringcase(lcm_cmdtype, steamer):
3263 newchar->type = TYPE_STEAMER;
3264 break;
3265 stringcase(lcm_cmdtype, pshot):
3266 newchar->type = TYPE_SHOT;
3267 if(newchar->aimove == -1)
3268 newchar->aimove = 0;
3269 newchar->aimove |= AIMOVE1_ARROW;
3270 if(!newchar->offscreenkill)
3271 newchar->offscreenkill = 200;
3272 newchar->subject_to_hole = 0;
3273 newchar->subject_to_gravity = 1;
3274 newchar->subject_to_wall = 0;
3275 newchar->subject_to_platform = 0;
3276 newchar->subject_to_screen = 0;
3277 newchar->subject_to_minz = 1;
3278 newchar->subject_to_maxz = 1;
3279 newchar->subject_to_platform = 0;
3280 newchar->no_adjust_base = 1;
3281 break;
3282 stringcase(lcm_cmdtype, trap):
3283 newchar->type = TYPE_TRAP;
3284 newchar->subject_to_wall = 1;
3285 newchar->subject_to_platform = 1;
3286 newchar->subject_to_hole = 1;
3287 newchar->subject_to_gravity = 1;
3288 newchar->subject_to_minz = 1;
3289 newchar->subject_to_maxz = 1;
3290 newchar->no_adjust_base = 0;
3291 break;
3292 stringcase(lcm_cmdtype, text):
3293 // Used for displaying text/images and freezing the screen
3294 newchar->type = TYPE_TEXTBOX;
3295 newchar->subject_to_gravity = 0;
3296 newchar->subject_to_minz = 1;
3297 newchar->subject_to_maxz = 1;
3298 break;
3299 stringcase(lcm_cmdtype, endlevel):
3300 // Used for ending the level when the players reach a certain point
3301 newchar->type = TYPE_ENDLEVEL;
3302 newchar->subject_to_wall = 1;
3303 newchar->subject_to_platform = 1;
3304 newchar->subject_to_hole = 1;
3305 newchar->subject_to_obstacle = 1;
3306 newchar->subject_to_gravity = 1;
3307 break;
3308 stringcase(lcm_cmdtype, npc):
3309 newchar->type = TYPE_NPC;
3310 newchar->bounce = 1;
3311 newchar->subject_to_wall = 1;
3312 newchar->subject_to_platform = 1;
3313 newchar->subject_to_hole = 1;
3314 newchar->subject_to_obstacle = 1;
3315 newchar->subject_to_gravity = 1;
3316 newchar->subject_to_minz = 1;
3317 newchar->subject_to_maxz = 1;
3318 newchar->no_adjust_base = 0;
3319 break;
3320 stringcase(lcm_cmdtype, panel):
3321 newchar->type = TYPE_PANEL;
3322 newchar->antigravity = 1.0;
3323 newchar->subject_to_gravity = 1;
3324 newchar->no_adjust_base = 1;
3325 break;
3326 default:
3327 shutdown(1, "Model '%s' has invalid type: '%s'", filename, value);
3331 //stringswitch_gen add lcm_cmdsubtype "biker"
3332 //stringswitch_gen add lcm_cmdsubtype "arrow"
3333 //stringswitch_gen add lcm_cmdsubtype "notgrab"
3334 //stringswitch_gen add lcm_cmdsubtype "touch"
3335 //stringswitch_gen add lcm_cmdsubtype "weapon"
3336 //stringswitch_gen add lcm_cmdsubtype "noskip"
3337 //stringswitch_gen add lcm_cmdsubtype "flydie"
3338 //stringswitch_gen add lcm_cmdsubtype "both"
3339 //stringswitch_gen add lcm_cmdsubtype "project"
3340 //stringswitch_gen add lcm_cmdsubtype "follow"
3341 //stringswitch_gen add lcm_cmdsubtype "chase"
3343 #include "stringswitch_impl_lcm_cmdsubtype.c"
3345 void lcmHandleCommandSubtype(ArgList * arglist, s_model * newchar, char *filename) {
3346 char *value = GET_ARGP(1);
3347 int i;
3348 lc(value, GET_ARGP_LEN(1));
3349 stringswitch_d(lcm_cmdsubtype, value) {
3350 stringcase(lcm_cmdsubtype, biker):
3351 newchar->subtype = SUBTYPE_BIKER;
3352 if(newchar->aimove == -1)
3353 newchar->aimove = 0;
3354 newchar->aimove |= AIMOVE1_BIKER;
3355 for(i = 0; i < MAX_ATKS; i++)
3356 newchar->defense_factors[i] = 2;
3357 if(!newchar->offscreenkill)
3358 newchar->offscreenkill = 300;
3359 newchar->subject_to_hole = 1;
3360 newchar->subject_to_gravity = 1;
3361 newchar->subject_to_wall = 0;
3362 newchar->subject_to_platform = 0;
3363 newchar->subject_to_screen = 0;
3364 newchar->subject_to_minz = 1;
3365 newchar->subject_to_maxz = 1;
3366 newchar->subject_to_platform = 0;
3367 newchar->no_adjust_base = 0;
3368 break;
3369 stringcase(lcm_cmdsubtype, arrow):
3370 newchar->subtype = SUBTYPE_ARROW; // 7-1-2005 Arrow type
3371 if(newchar->aimove == -1)
3372 newchar->aimove = 0;
3373 newchar->aimove |= AIMOVE1_ARROW;
3374 if(!newchar->offscreenkill)
3375 newchar->offscreenkill = 200;
3376 newchar->subject_to_hole = 0;
3377 newchar->subject_to_gravity = 1;
3378 newchar->subject_to_wall = 0;
3379 newchar->subject_to_platform = 0;
3380 newchar->subject_to_screen = 0;
3381 newchar->subject_to_minz = 1;
3382 newchar->subject_to_maxz = 1;
3383 newchar->subject_to_platform = 0;
3384 newchar->no_adjust_base = 1;
3385 break;
3386 stringcase(lcm_cmdsubtype, notgrab):
3387 newchar->subtype = SUBTYPE_NOTGRAB; // 7-1-2005 notgrab type
3388 break;
3389 stringcase(lcm_cmdsubtype, touch):
3390 newchar->subtype = SUBTYPE_TOUCH; // 7-1-2005 notgrab type
3391 break;
3392 stringcase(lcm_cmdsubtype, weapon):
3393 newchar->subtype = SUBTYPE_WEAPON; // 7-1-2005 notgrab type
3394 break;
3395 stringcase(lcm_cmdsubtype, noskip):
3396 // Text animation cannot be skipped if subtype noskip
3397 newchar->subtype = SUBTYPE_NOSKIP;
3398 break;
3399 stringcase(lcm_cmdsubtype, flydie):
3400 // Obstacle will fly across the screen when hit if subtype flydie
3401 newchar->subtype = SUBTYPE_FLYDIE;
3402 break;
3403 stringcase(lcm_cmdsubtype, both):
3404 newchar->subtype = SUBTYPE_BOTH;
3405 break;
3406 stringcase(lcm_cmdsubtype, project):
3407 newchar->subtype = SUBTYPE_PROJECTILE;
3408 break;
3409 stringcase(lcm_cmdsubtype, follow):
3410 newchar->subtype = SUBTYPE_FOLLOW;
3411 break;
3412 stringcase(lcm_cmdsubtype, chase):
3413 newchar->subtype = SUBTYPE_CHASE;
3414 break;
3415 default:
3416 shutdown(1, "Model '%s' has invalid subtype: '%s'", filename, value);
3420 void lcmHandleCommandSmartbomb(ArgList * arglist, s_model * newchar, char *filename) {
3421 //smartbomb now use a normal attack box
3422 if(!newchar->smartbomb) {
3423 newchar->smartbomb = malloc(sizeof(s_attack));
3424 *(newchar->smartbomb) = emptyattack;
3425 } else
3426 shutdown(1, "Model '%s' has multiple smartbomb commands defined.", filename);
3428 newchar->smartbomb->attack_force = atoi(GET_ARGP(1)); // Special force
3429 newchar->smartbomb->attack_type = atoi(GET_ARGP(2)); // Special attack type
3430 newchar->smartbomb->attack_drop = 1; //by default
3431 newchar->smartbomb->dropv[0] = 3;
3433 if(newchar->smartbomb->attack_type == ATK_BLAST) {
3434 newchar->smartbomb->blast = 1;
3435 newchar->smartbomb->dropv[1] = 2.5;
3436 } else {
3437 newchar->smartbomb->dropv[1] = (float) 1.2;
3440 if(newchar->smartbomb->attack_type == ATK_FREEZE) {
3441 newchar->smartbomb->freeze = 1;
3442 newchar->smartbomb->forcemap = -1;
3443 newchar->smartbomb->attack_drop = 0;
3444 } else if(newchar->smartbomb->attack_type == ATK_STEAL) {
3445 newchar->smartbomb->steal = 1;
3448 if(newchar->type == TYPE_ITEM) {
3449 newchar->dofreeze = 0; // Items don't animate
3450 newchar->smartbomb->freezetime = atoi(GET_ARGP(3)) * GAME_SPEED;
3451 } else {
3452 newchar->dofreeze = atoi(GET_ARGP(3)); // Are all animations frozen during special
3453 newchar->smartbomb->freezetime = atoi(GET_ARGP(4)) * GAME_SPEED;
3457 //stringswitch_gen add lcm_cmdhostile "enemy"
3458 //stringswitch_gen add lcm_cmdhostile "player"
3459 //stringswitch_gen add lcm_cmdhostile "obstacle"
3460 //stringswitch_gen add lcm_cmdhostile "shot"
3461 //stringswitch_gen add lcm_cmdhostile "npc"
3463 #include "stringswitch_impl_lcm_cmdhostile.c"
3465 void lcmHandleCommandHostile(ArgList * arglist, s_model * newchar) {
3466 int i = 1;
3467 char *value = GET_ARGP(i);
3468 lc(value, GET_ARGP_LEN(i));
3469 newchar->hostile = 0;
3470 while(value && value[0]) {
3471 stringswitch_d(lcm_cmdhostile, value) {
3472 stringcase(lcm_cmdhostile, enemy):
3473 newchar->hostile |= TYPE_ENEMY;
3474 break;
3475 stringcase(lcm_cmdhostile, player):
3476 newchar->hostile |= TYPE_PLAYER;
3477 break;
3478 stringcase(lcm_cmdhostile, obstacle):
3479 newchar->hostile |= TYPE_OBSTACLE;
3480 break;
3481 stringcase(lcm_cmdhostile, shot):
3482 newchar->hostile |= TYPE_SHOT;
3483 break;
3484 stringcase(lcm_cmdhostile, npc):
3485 newchar->hostile |= TYPE_NPC;
3486 break;
3487 default:
3488 break;
3490 i++;
3491 value = GET_ARGP(i);
3492 lc(value, GET_ARGP_LEN(i));
3496 //stringswitch_gen add lcm_cmdcandamage "enemy"
3497 //stringswitch_gen add lcm_cmdcandamage "player"
3498 //stringswitch_gen add lcm_cmdcandamage "obstacle"
3499 //stringswitch_gen add lcm_cmdcandamage "shot"
3500 //stringswitch_gen add lcm_cmdcandamage "npc"
3501 //stringswitch_gen add lcm_cmdcandamage "ground"
3503 #include "stringswitch_impl_lcm_cmdcandamage.c"
3505 void lcmHandleCommandCandamage(ArgList * arglist, s_model * newchar) {
3506 int i = 1;
3507 char *value = GET_ARGP(i);
3508 lc(value, GET_ARGP_LEN(i));
3509 newchar->candamage = 0;
3511 while(value && value[0]) {
3512 stringswitch_d(lcm_cmdcandamage, value) {
3513 stringcase(lcm_cmdcandamage, enemy):
3514 newchar->candamage |= TYPE_ENEMY;
3515 break;
3516 stringcase(lcm_cmdcandamage, player):
3517 newchar->candamage |= TYPE_PLAYER;
3518 break;
3519 stringcase(lcm_cmdcandamage, obstacle):
3520 newchar->candamage |= TYPE_OBSTACLE;
3521 break;
3522 stringcase(lcm_cmdcandamage, shot):
3523 newchar->candamage |= TYPE_SHOT;
3524 break;
3525 stringcase(lcm_cmdcandamage, npc):
3526 newchar->candamage |= TYPE_NPC;
3527 break;
3528 stringcase(lcm_cmdcandamage, ground):
3529 newchar->ground = 1;
3530 break;
3532 i++;
3533 value = GET_ARGP(i);
3534 lc(value, GET_ARGP_LEN(i));
3538 //stringswitch_gen add lcm_cmdprojectilehit "enemy"
3539 //stringswitch_gen add lcm_cmdprojectilehit "player"
3540 //stringswitch_gen add lcm_cmdprojectilehit "obstacle"
3541 //stringswitch_gen add lcm_cmdprojectilehit "shot"
3542 //stringswitch_gen add lcm_cmdprojectilehit "npc"
3544 #include "stringswitch_impl_lcm_cmdprojectilehit.c"
3546 void lcmHandleCommandProjectilehit(ArgList * arglist, s_model * newchar) {
3547 int i = 1;
3548 char *value = GET_ARGP(i);
3549 lc(value, GET_ARGP_LEN(i));
3550 newchar->projectilehit = 0;
3552 while(value && value[0]) {
3553 stringswitch_d(lcm_cmdprojectilehit, value) {
3554 stringcase(lcm_cmdprojectilehit, enemy):
3555 newchar->projectilehit |= TYPE_ENEMY;
3556 break;
3557 stringcase(lcm_cmdprojectilehit, player):
3558 newchar->projectilehit |= TYPE_PLAYER;
3559 break;
3560 stringcase(lcm_cmdprojectilehit, obstacle):
3561 newchar->projectilehit |= TYPE_OBSTACLE;
3562 break;
3563 stringcase(lcm_cmdprojectilehit, shot):
3564 newchar->projectilehit |= TYPE_SHOT;
3565 break;
3566 stringcase(lcm_cmdprojectilehit, npc):
3567 newchar->projectilehit |= TYPE_NPC;
3568 break;
3570 i++;
3571 value = GET_ARGP(i);
3572 lc(value, GET_ARGP_LEN(i));
3576 //stringswitch_gen add lcm_cmdaimove "normal"
3577 //stringswitch_gen add lcm_cmdaimove "chase"
3578 //stringswitch_gen add lcm_cmdaimove "chasex"
3579 //stringswitch_gen add lcm_cmdaimove "chasez"
3580 //stringswitch_gen add lcm_cmdaimove "avoid"
3581 //stringswitch_gen add lcm_cmdaimove "avoidx"
3582 //stringswitch_gen add lcm_cmdaimove "avoidz"
3583 //stringswitch_gen add lcm_cmdaimove "wander"
3584 //stringswitch_gen add lcm_cmdaimove "biker"
3585 //stringswitch_gen add lcm_cmdaimove "arrow"
3586 //stringswitch_gen add lcm_cmdaimove "star"
3587 //stringswitch_gen add lcm_cmdaimove "bomb"
3588 //stringswitch_gen add lcm_cmdaimove "nomove"
3590 #include "stringswitch_impl_lcm_cmdaimove.c"
3592 void lcmHandleCommandAimove(ArgList * arglist, s_model * newchar, int *aimoveset, char *filename) {
3593 char *value = GET_ARGP(1);
3594 lc(value, GET_ARGP_LEN(1));
3595 if(!*aimoveset) {
3596 newchar->aimove = 0;
3597 *aimoveset = 1;
3599 //main A.I. move switches
3600 if(value && value[0]) {
3601 stringswitch_d(lcm_cmdaimove, value) {
3602 stringcase(lcm_cmdaimove, normal):
3603 newchar->aimove |= AIMOVE1_NORMAL;
3604 break;
3605 stringcase(lcm_cmdaimove, chase):
3606 newchar->aimove |= AIMOVE1_CHASE;
3607 break;
3608 stringcase(lcm_cmdaimove, chasex):
3609 newchar->aimove |= AIMOVE1_CHASEX;
3610 break;
3611 stringcase(lcm_cmdaimove, chasez):
3612 newchar->aimove |= AIMOVE1_CHASEZ;
3613 break;
3614 stringcase(lcm_cmdaimove, avoid):
3615 newchar->aimove |= AIMOVE1_AVOID;
3616 break;
3617 stringcase(lcm_cmdaimove, avoidx):
3618 newchar->aimove |= AIMOVE1_AVOIDX;
3619 break;
3620 stringcase(lcm_cmdaimove, avoidz):
3621 newchar->aimove |= AIMOVE1_AVOIDZ;
3622 break;
3623 stringcase(lcm_cmdaimove, wander):
3624 newchar->aimove |= AIMOVE1_WANDER;
3625 break;
3626 stringcase(lcm_cmdaimove, biker):
3627 newchar->aimove |= AIMOVE1_BIKER;
3628 break;
3629 stringcase(lcm_cmdaimove, arrow):
3630 newchar->aimove |= AIMOVE1_ARROW;
3631 if(!newchar->offscreenkill)
3632 newchar->offscreenkill = 200;
3633 break;
3634 stringcase(lcm_cmdaimove, star):
3635 newchar->aimove |= AIMOVE1_STAR;
3636 break;
3637 stringcase(lcm_cmdaimove, bomb):
3638 newchar->aimove |= AIMOVE1_BOMB;
3639 break;
3640 stringcase(lcm_cmdaimove, nomove):
3641 newchar->aimove |= AIMOVE1_NOMOVE;
3642 break;
3643 default:
3644 shutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename, value);
3647 value = GET_ARGP(2);
3648 //sub A.I. move switches
3649 if(value && value[0]) {
3650 if(stricmp(value, "normal") == 0) {
3651 newchar->aimove |= AIMOVE2_NORMAL;
3652 } else if(stricmp(value, "ignoreholes") == 0) {
3653 newchar->aimove |= AIMOVE2_IGNOREHOLES;
3654 } else
3655 shutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename, value);
3659 void lcmHandleCommandWeapons(ArgList * arglist, s_model * newchar) {
3660 int weap;
3661 char *value;
3662 int last = 0;
3663 if(!newchar->weapon) {
3664 newchar->weapon = malloc(sizeof(*newchar->weapon));
3665 memset(newchar->weapon, 0xFF, sizeof(*newchar->weapon));
3666 newchar->ownweapons = 1;
3668 for(weap = 0; weap < MAX_WEAPONS; weap++) {
3669 value = GET_ARGP(weap + 1);
3670 if(value[0]) {
3671 if(stricmp(value, "none") != 0) {
3672 (*newchar->weapon)[weap] = get_cached_model_index(value);
3673 } else { // make empty weapon slots 2007-2-16
3674 (*newchar->weapon)[weap] = -1;
3676 last = weap;
3677 } else
3678 (*newchar->weapon)[weap] = (*newchar->weapon)[last];
3681 void lcmHandleCommandScripts(ArgList * arglist, Script * script, char *scriptname, char *filename) {
3682 Script_Init(script, scriptname, 0);
3683 if(load_script(script, GET_ARGP(1)))
3684 Script_Compile(script);
3685 else
3686 shutdown(1, "Unable to load %s '%s' in file '%s'.\n", scriptname, GET_ARGP(1), filename);
3689 void lcmSetCachedModelIndexOrMinusOne(char* value, int* dest) {
3690 if(stricmp(value, "none") == 0)
3691 *dest = -1;
3692 else
3693 *dest = get_cached_model_index(value);
3696 //stringswitch_gen add lcm_cmdcom "u"
3697 //stringswitch_gen add lcm_cmdcom "d"
3698 //stringswitch_gen add lcm_cmdcom "f"
3699 //stringswitch_gen add lcm_cmdcom "b"
3700 //stringswitch_gen add lcm_cmdcom "a"
3701 //stringswitch_gen add lcm_cmdcom "a2"
3702 //stringswitch_gen add lcm_cmdcom "a3"
3703 //stringswitch_gen add lcm_cmdcom "a4"
3704 //stringswitch_gen add lcm_cmdcom "j"
3705 //stringswitch_gen add lcm_cmdcom "s"
3706 //stringswitch_gen add lcm_cmdcom "k"
3708 #include "stringswitch_impl_lcm_cmdcom.c"
3710 // returns 1 if one of the values in the stringswitch was found, 0 otherwise.
3711 static int switchComAndCancelCommon(char** value, s_model *newchar, int i) {
3712 stringswitch_d(lcm_cmdcom, (*value)) {
3713 stringcase(lcm_cmdcom, u):
3714 newchar->special[newchar->specials_loaded][i] = FLAG_MOVEUP;
3715 break;
3716 stringcase(lcm_cmdcom, d):
3717 newchar->special[newchar->specials_loaded][i] = FLAG_MOVEDOWN;
3718 break;
3719 stringcase(lcm_cmdcom, f):
3720 newchar->special[newchar->specials_loaded][i] = FLAG_FORWARD;
3721 break;
3722 stringcase(lcm_cmdcom, b):
3723 newchar->special[newchar->specials_loaded][i] = FLAG_BACKWARD;
3724 break;
3725 stringcase(lcm_cmdcom, a):
3726 newchar->special[newchar->specials_loaded][i] = FLAG_ATTACK;
3727 break;
3728 stringcase(lcm_cmdcom, a2):
3729 newchar->special[newchar->specials_loaded][i] = FLAG_ATTACK2;
3730 break;
3731 stringcase(lcm_cmdcom, a3):
3732 newchar->special[newchar->specials_loaded][i] = FLAG_ATTACK3;
3733 break;
3734 stringcase(lcm_cmdcom, a4):
3735 newchar->special[newchar->specials_loaded][i] = FLAG_ATTACK4;
3736 break;
3737 stringcase(lcm_cmdcom, j):
3738 newchar->special[newchar->specials_loaded][i] = FLAG_JUMP;
3739 break;
3740 stringcase(lcm_cmdcom, s):
3741 stringcase(lcm_cmdcom, k):
3742 newchar->special[newchar->specials_loaded][i] = FLAG_SPECIAL;
3743 break;
3744 default:
3745 return 0;
3747 return 1;
3750 // returns: 0: OK, -1: fatal, 1:warning, proceed to next line
3751 int lcmHandleCommandCom(ArgList * arglist, s_model *newchar, char** value, char** shutdownmessage) {
3752 // Section for custom freespecials starts here
3753 int tempInt;
3754 int i;
3755 int t;
3756 for(i = 0, t = 1; i < MAX_SPECIAL_INPUTS - 3; i++, t++) {
3757 *value = GET_ARGP(t);
3758 if(!(*value)[0])
3759 break;
3760 lc(*value, GET_ARGP_LEN(t));
3761 if(!switchComAndCancelCommon(value, newchar, i)) {
3762 if((!strnicmp(*value, "freespecial", 11)) &&
3764 !(*value)[11] ||
3765 ((*value)[11] >= '1' && (*value)[11] <= '9')
3768 tempInt = atoi((*value) + 11);
3769 if(tempInt < 1)
3770 tempInt = 1;
3771 newchar->special[newchar->specials_loaded]
3772 [MAX_SPECIAL_INPUTS - 2] = dyn_anims.animspecials[tempInt - 1];
3773 } else
3774 return 1;
3777 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 3] = i - 1; // max steps
3778 newchar->specials_loaded++;
3779 if(newchar->specials_loaded > dyn_anim_custom_maxvalues.max_freespecials) {
3780 *shutdownmessage = "Too many Freespecials and/or Cancels. Please increase Maxfreespecials"; // OX. This is to catch freespecials that use same animation.
3781 return -1;
3783 return 0;
3786 // returns: 0: OK, -1: fatal, 1:warning, proceed to next line
3787 int lcmHandleCommandCancel(ArgList * arglist, s_model *newchar, s_anim* newanim, char** value, int ani_id, char** shutdownmessage, char* filename, char* command) {
3788 int i; // OX. Modified copy/paste of COM settings code
3789 int t;
3790 int tempInt;
3791 newanim->cancel = 3;
3792 for(i = 0, t = 4; i < MAX_SPECIAL_INPUTS - 6; i++, t++) {
3793 *value = GET_ARGP(t);
3794 if(!(*value)[0])
3795 break;
3796 lc((*value), GET_ARGP_LEN(t));
3797 if(!switchComAndCancelCommon(value, newchar, i)) {
3798 if(strnicmp((*value), "freespecial", 11) == 0
3799 && (!(*value)[11]
3800 || ((*value)[11] >= '1' && (*value)[11] <= '9'))) {
3801 tempInt = atoi((*value) + 11);
3802 if(tempInt < 1)
3803 tempInt = 1;
3804 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 5] = dyn_anims.animspecials[tempInt - 1];
3805 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 7] = GET_INT_ARGP(1); // stores start frame
3806 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 8] = GET_INT_ARGP(2); // stores end frame
3807 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 9] = ani_id; // stores current anim
3808 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 10] = GET_INT_ARGP(3); // stores hits
3809 } else {
3810 *shutdownmessage = "Invalid cancel command!";
3811 return -1;
3816 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 6] = i - 1; // max steps
3817 newchar->specials_loaded++;
3818 if(newchar->specials_loaded > dyn_anim_custom_maxvalues.max_freespecials) {
3819 *shutdownmessage =
3820 "Too many Freespecials and/or Cancels. Please increase Maxfreespecials";
3821 return -1;
3823 return 0;
3828 //stringswitch_gen add lcm_cmdanim "waiting"
3829 //stringswitch_gen add lcm_cmdanim "sleep"
3830 //stringswitch_gen add lcm_cmdanim "run"
3831 //stringswitch_gen add lcm_cmdanim "jump"
3832 //stringswitch_gen add lcm_cmdanim "duck"
3833 //stringswitch_gen add lcm_cmdanim "land"
3834 //stringswitch_gen add lcm_cmdanim "pain"
3835 //stringswitch_gen add lcm_cmdanim "spain"
3836 //stringswitch_gen add lcm_cmdanim "bpain"
3837 //stringswitch_gen add lcm_cmdanim "fall"
3838 //stringswitch_gen add lcm_cmdanim "shock"
3839 //stringswitch_gen add lcm_cmdanim "burn"
3840 //stringswitch_gen add lcm_cmdanim "death"
3841 //stringswitch_gen add lcm_cmdanim "sdie"
3842 //stringswitch_gen add lcm_cmdanim "bdie"
3843 //stringswitch_gen add lcm_cmdanim "chipdeath"
3844 //stringswitch_gen add lcm_cmdanim "guardbreak"
3845 //stringswitch_gen add lcm_cmdanim "riseb"
3846 //stringswitch_gen add lcm_cmdanim "rises"
3847 //stringswitch_gen add lcm_cmdanim "rise"
3848 //stringswitch_gen add lcm_cmdanim "riseattackb"
3849 //stringswitch_gen add lcm_cmdanim "riseattacks"
3850 //stringswitch_gen add lcm_cmdanim "riseattack"
3851 //stringswitch_gen add lcm_cmdanim "select"
3852 //stringswitch_gen add lcm_cmdanim "throwattack"
3853 //stringswitch_gen add lcm_cmdanim "upper"
3854 //stringswitch_gen add lcm_cmdanim "cant"
3855 //stringswitch_gen add lcm_cmdanim "jumpcant"
3856 //stringswitch_gen add lcm_cmdanim "charge"
3857 //stringswitch_gen add lcm_cmdanim "faint"
3858 //stringswitch_gen add lcm_cmdanim "dodge"
3859 //stringswitch_gen add lcm_cmdanim "special"
3860 //stringswitch_gen add lcm_cmdanim "jumpspecial"
3861 //stringswitch_gen add lcm_cmdanim "jumpattack"
3862 //stringswitch_gen add lcm_cmdanim "jumpforward"
3863 //stringswitch_gen add lcm_cmdanim "runjumpattack"
3864 //stringswitch_gen add lcm_cmdanim "runattack"
3865 //stringswitch_gen add lcm_cmdanim "attackup"
3866 //stringswitch_gen add lcm_cmdanim "attackdown"
3867 //stringswitch_gen add lcm_cmdanim "attackforward"
3868 //stringswitch_gen add lcm_cmdanim "attackbackward"
3869 //stringswitch_gen add lcm_cmdanim "attackboth"
3870 //stringswitch_gen add lcm_cmdanim "get"
3871 //stringswitch_gen add lcm_cmdanim "grab"
3872 //stringswitch_gen add lcm_cmdanim "grabwalk"
3873 //stringswitch_gen add lcm_cmdanim "grabwalkup"
3874 //stringswitch_gen add lcm_cmdanim "grabwalkdown"
3875 //stringswitch_gen add lcm_cmdanim "grabbackwalk"
3876 //stringswitch_gen add lcm_cmdanim "grabturn"
3877 //stringswitch_gen add lcm_cmdanim "grabbed"
3878 //stringswitch_gen add lcm_cmdanim "grabbedwalk"
3879 //stringswitch_gen add lcm_cmdanim "grabbedwalkup"
3880 //stringswitch_gen add lcm_cmdanim "grabbedwalkdown"
3881 //stringswitch_gen add lcm_cmdanim "grabbedbackwalk"
3882 //stringswitch_gen add lcm_cmdanim "grabbedturn"
3883 //stringswitch_gen add lcm_cmdanim "grabattack"
3884 //stringswitch_gen add lcm_cmdanim "grabforward"
3885 //stringswitch_gen add lcm_cmdanim "grabbackward"
3886 //stringswitch_gen add lcm_cmdanim "grabup"
3887 //stringswitch_gen add lcm_cmdanim "grabdown"
3888 //stringswitch_gen add lcm_cmdanim "spawn"
3889 //stringswitch_gen add lcm_cmdanim "respawn"
3890 //stringswitch_gen add lcm_cmdanim "throw"
3891 //stringswitch_gen add lcm_cmdanim "block"
3892 //stringswitch_gen add lcm_cmdanim "chargeattack"
3893 //stringswitch_gen add lcm_cmdanim "vault"
3894 //stringswitch_gen add lcm_cmdanim "turn"
3895 //stringswitch_gen add lcm_cmdanim "forwardjump"
3896 //stringswitch_gen add lcm_cmdanim "runjump"
3897 //stringswitch_gen add lcm_cmdanim "jumpland"
3898 //stringswitch_gen add lcm_cmdanim "jumpdelay"
3899 //stringswitch_gen add lcm_cmdanim "hitwall"
3900 //stringswitch_gen add lcm_cmdanim "slide"
3901 //stringswitch_gen add lcm_cmdanim "runslide"
3902 //stringswitch_gen add lcm_cmdanim "blockpainb"
3903 //stringswitch_gen add lcm_cmdanim "blockpains"
3904 //stringswitch_gen add lcm_cmdanim "blockpain"
3905 //stringswitch_gen add lcm_cmdanim "duckattack"
3906 //stringswitch_gen add lcm_cmdanim "walkoff"
3907 //stringswitch_gen add lcm_cmdanim "attack"
3908 //stringswitch_gen add lcm_cmdanim "walk"
3909 //stringswitch_gen add lcm_cmdanim "up"
3910 //stringswitch_gen add lcm_cmdanim "down"
3911 //stringswitch_gen add lcm_cmdanim "backwalk"
3912 //stringswitch_gen add lcm_cmdanim "idle"
3913 //stringswitch_gen add lcm_cmdanim "follow"
3914 //stringswitch_gen add lcm_cmdanim "freespecial"
3917 #include "stringswitch_impl_lcm_cmdanim.c"
3919 // returns: 0: OK, -1: fatal, 1:warning, proceed to next line
3920 int lcmHandleCommandAnim(ArgList * arglist, s_model *newchar, s_anim **newanim, int *ani_id, char** value, char** shutdownmessage, s_attack* attack) {
3921 int endsWithNumber = 0;
3922 unsigned l = GET_ARGP_LEN(1);
3923 int commandIndex = 1;
3924 char lowercase_buf[32];
3926 *value = GET_ARGP(1);
3927 // if a command ends with a number, we set commandIndex to that number and remove it from the string
3928 // this assumes that the ANI_ enum members are ordered like ATTACK1, ATTACK2, etc.
3929 char_to_lower(lowercase_buf, *value, sizeof(lowercase_buf));
3930 while(l > 0 && lowercase_buf[l - 1] >= '0' && lowercase_buf[l - 1] <= '9') {
3931 endsWithNumber = 1;
3932 l--;
3934 if(endsWithNumber) {
3935 commandIndex = atoi(lowercase_buf + l);
3936 lowercase_buf[l] = 0;
3937 if(commandIndex < 1)
3938 commandIndex = 1;
3941 // Create new animation
3942 (*newanim) = alloc_anim();
3943 if(!(*newanim)) {
3944 *shutdownmessage = (char*) E_OUT_OF_MEMORY;
3945 return -1;
3947 (*newanim)->model_index = newchar->index;
3948 // Reset vars
3950 if(!(*newanim)->range[0])
3951 (*newanim)->range[0] = -10;
3952 (*newanim)->range[1] = (int) newchar->jumpheight * 20; //30-12-2004 default range affected by jump height
3953 (*newanim)->range[2] = (int) -newchar->grabdistance / 3; //zmin
3954 (*newanim)->range[3] = (int) newchar->grabdistance / 3; //zmax
3955 (*newanim)->range[4] = -1000; //amin
3956 (*newanim)->range[5] = 1000; //amax
3957 (*newanim)->range[6] = -1000; //Base min.
3958 (*newanim)->range[7] = 1000; //Base max.
3960 (*newanim)->jumpv = 0; // Default disabled
3961 (*newanim)->fastattack = 0;
3962 (*newanim)->energycost[1] = 0; //MP only.
3963 (*newanim)->energycost[2] = 0; //Disable flag.
3964 (*newanim)->chargetime = 2; // Default for backwards compatibility
3965 (*newanim)->shootframe = -1;
3966 (*newanim)->throwframe = -1;
3967 (*newanim)->tossframe = -1; // this get 1 of weapons numshots shots in the animation that you want(normaly the last)by tails
3968 (*newanim)->jumpframe = -1;
3969 (*newanim)->flipframe = -1;
3970 (*newanim)->attackone = -1;
3971 (*newanim)->dive[0] = (*newanim)->dive[1] = 0;
3972 (*newanim)->followanim = 0; // Default disabled
3973 (*newanim)->followcond = 0;
3974 (*newanim)->counterframe[0] = -1; //Start frame.
3975 (*newanim)->counterframe[1] = -1; //End frame.
3976 (*newanim)->counterframe[2] = 0; //Counter cond.
3977 (*newanim)->counterframe[3] = 0; //Counter damage.
3978 (*newanim)->unsummonframe = -1;
3979 (*newanim)->landframe[0] = -1;
3980 (*newanim)->dropframe = -1;
3981 (*newanim)->cancel = 0; // OX. For cancelling anims into a freespecial. 0 by default , 3 when enabled. IMPORTANT!! Must stay as it is!
3982 (*newanim)->animhits = 0; //OX counts hits on a per anim basis for cancels.
3983 (*newanim)->subentity = (*newanim)->custbomb = (*newanim)->custknife =
3984 (*newanim)->custstar = (*newanim)->custpshotno = -1;
3985 memset((*newanim)->quakeframe, 0, sizeof((*newanim)->quakeframe));
3987 #define AEM(string, enum_member) [stringswitch_enumerator_member_name(lcm_cmdanim, string)] = enum_member
3988 static const ani_types enum_mapping[] = {
3989 AEM(waiting, ANI_SELECT),
3990 AEM(sleep, ANI_SLEEP),
3991 AEM(run, ANI_RUN),
3992 AEM(jump, ANI_JUMP),
3993 AEM(duck, ANI_DUCK),
3994 AEM(land, ANI_LAND),
3995 AEM(spain, ANI_SHOCKPAIN),
3996 AEM(bpain, ANI_BURNPAIN),
3997 AEM(shock, ANI_SHOCK),
3998 AEM(burn, ANI_BURN),
3999 AEM(sdie, ANI_SHOCKDIE),
4000 AEM(bdie, ANI_BURNDIE),
4001 AEM(chipdeath, ANI_CHIPDEATH),
4002 AEM(guardbreak, ANI_GUARDBREAK),
4003 AEM(riseb, ANI_RISEB),
4004 AEM(rises, ANI_RISES),
4005 AEM(riseattackb, ANI_RISEATTACKB),
4006 AEM(riseattacks, ANI_RISEATTACKS),
4007 AEM(select, ANI_PICK),
4008 AEM(throwattack, ANI_THROWATTACK),
4009 AEM(upper, ANI_UPPER),
4010 AEM(cant, ANI_CANT),
4011 AEM(jumpcant, ANI_JUMPCANT),
4012 AEM(charge, ANI_CHARGE),
4013 AEM(faint, ANI_FAINT),
4014 AEM(dodge, ANI_DODGE),
4015 AEM(jumpforward, ANI_JUMPFORWARD),
4016 AEM(runjumpattack, ANI_RUNJUMPATTACK),
4017 AEM(runattack, ANI_RUNATTACK),
4018 AEM(attackup, ANI_ATTACKUP),
4019 AEM(attackdown, ANI_ATTACKDOWN),
4020 AEM(attackforward, ANI_ATTACKFORWARD),
4021 AEM(attackbackward, ANI_ATTACKBACKWARD),
4022 AEM(attackboth, ANI_ATTACKBOTH),
4023 AEM(get, ANI_GET),
4024 AEM(grab, ANI_GRAB),
4025 AEM(grabwalk, ANI_GRABWALK),
4026 AEM(grabwalkup, ANI_GRABWALKUP),
4027 AEM(grabwalkdown, ANI_GRABWALKDOWN),
4028 AEM(grabbackwalk, ANI_GRABBACKWALK),
4029 AEM(grabturn, ANI_GRABTURN),
4030 AEM(grabbed, ANI_GRABBED),
4031 AEM(grabbedwalk, ANI_GRABBEDWALK),
4032 AEM(grabbedwalkup, ANI_GRABWALKUP),
4033 AEM(grabbedwalkdown, ANI_GRABWALKDOWN),
4034 AEM(grabbedbackwalk, ANI_GRABBEDBACKWALK),
4035 AEM(grabbedturn, ANI_GRABBEDTURN),
4036 AEM(spawn, ANI_SPAWN),
4037 AEM(respawn, ANI_RESPAWN),
4038 AEM(throw, ANI_THROW),
4039 AEM(block, ANI_BLOCK),
4040 AEM(chargeattack, ANI_CHARGEATTACK),
4041 AEM(vault, ANI_VAULT),
4042 AEM(turn, ANI_TURN),
4043 AEM(forwardjump, ANI_FORWARDJUMP),
4044 AEM(runjump, ANI_RUNJUMP),
4045 AEM(jumpland, ANI_JUMPLAND),
4046 AEM(jumpdelay, ANI_JUMPDELAY),
4047 AEM(hitwall, ANI_HITWALL),
4048 AEM(slide, ANI_SLIDE),
4049 AEM(runslide, ANI_RUNSLIDE),
4050 AEM(blockpainb, ANI_BLOCKPAINB),
4051 AEM(blockpains, ANI_BLOCKPAINS),
4052 AEM(duckattack, ANI_DUCKATTACK),
4053 AEM(walkoff, ANI_WALKOFF),
4054 AEM(attack, ANI_ATTACK),
4055 AEM(walk, ANI_WALK),
4056 AEM(up, ANI_UP),
4057 AEM(down, ANI_DOWN),
4058 AEM(backwalk, ANI_BACKWALK),
4059 AEM(idle, ANI_IDLE),
4060 AEM(follow, ANI_FOLLOW),
4061 AEM(jumpattack, ANI_JUMPATTACK),
4062 AEM(grabattack, ANI_GRABATTACK),
4063 AEM(grabforward, ANI_GRABFORWARD),
4064 AEM(grabbackward, ANI_GRABBACKWARD),
4065 AEM(grabup, ANI_GRABUP),
4066 AEM(grabdown, ANI_GRABDOWN),
4067 AEM(jumpspecial, ANI_JUMPSPECIAL),
4068 AEM(special, ANI_SPECIAL),
4069 AEM(pain, ANI_PAIN),
4070 AEM(rise, ANI_RISE),
4071 AEM(death, ANI_DIE),
4072 AEM(fall, ANI_FALL),
4073 AEM(riseattack, ANI_RISEATTACK),
4074 AEM(blockpain, ANI_BLOCKPAIN),
4075 AEM(freespecial, ANI_FREESPECIAL),
4077 #undef AEM
4079 int strswitch_result = get_stringswitch_value(lcm_cmdanim, lowercase_buf, l);
4080 if (strswitch_result == stringswitch_enumerator_default_member_name(lcm_cmdanim)) return 1;
4081 (*ani_id) = enum_mapping[strswitch_result];
4082 (*ani_id) += (commandIndex - 1);
4084 switch(strswitch_result) {
4085 stringcase(lcm_cmdanim, jump):
4086 (*newanim)->range[0] = 50; // Used for enemies that jump on walls
4087 (*newanim)->range[1] = 60; // Used for enemies that jump on walls
4088 break;
4089 stringcase(lcm_cmdanim, burn):
4090 stringcase(lcm_cmdanim, shock):
4091 (*newanim)->bounce = 4;
4092 break;
4093 stringcase(lcm_cmdanim, upper):
4094 attack->counterattack = 100; //default to 100
4095 (*newanim)->range[0] = -10;
4096 (*newanim)->range[1] = 120;
4097 break;
4098 stringcase(lcm_cmdanim, block):
4099 // Now enemies can block attacks on occasion
4100 (*newanim)->range[0] = 1;
4101 (*newanim)->range[1] = 100;
4102 break;
4103 stringcase(lcm_cmdanim, attack):
4104 (*ani_id) = dyn_anims.animattacks[commandIndex - 1];
4105 break;
4106 stringcase(lcm_cmdanim, walk):
4107 (*ani_id) = dyn_anims.animwalks[commandIndex - 1];
4108 break;
4109 stringcase(lcm_cmdanim, up):
4110 (*ani_id) = dyn_anims.animups[commandIndex - 1];
4111 break;
4112 stringcase(lcm_cmdanim, down):
4113 (*ani_id) = dyn_anims.animdowns[commandIndex - 1];
4114 break;
4115 stringcase(lcm_cmdanim, backwalk):
4116 (*ani_id) = dyn_anims.animbackwalks[commandIndex - 1];
4117 break;
4118 stringcase(lcm_cmdanim, idle):
4119 (*ani_id) = dyn_anims.animidles[commandIndex - 1];
4120 break;
4121 stringcase(lcm_cmdanim, follow):
4122 (*ani_id) = dyn_anims.animfollows[commandIndex - 1];
4123 break;
4124 stringcase(lcm_cmdanim, jumpattack):
4125 if(commandIndex == 1 && newchar->jumpheight == 4) {
4126 (*newanim)->range[0] = 150;
4127 (*newanim)->range[1] = 200;
4129 break;
4130 stringcase(lcm_cmdanim, grabattack):
4131 stringcase(lcm_cmdanim, grabforward):
4132 stringcase(lcm_cmdanim, grabbackward):
4133 stringcase(lcm_cmdanim, grabup):
4134 stringcase(lcm_cmdanim, grabdown):
4135 // New grab attack for when pressing down attack
4136 (*newanim)->attackone = 1;
4137 break;
4138 stringcase(lcm_cmdanim, special):
4139 if(commandIndex == 1) (*newanim)->energycost[0] = 6;
4140 break;
4141 stringcase(lcm_cmdanim, pain):
4142 if(!(commandIndex < 11)) {
4143 if(commandIndex < MAX_ATKS - STA_ATKS + 1)
4144 commandIndex = MAX_ATKS - STA_ATKS + 1;
4145 (*ani_id) = dyn_anims.animpains[commandIndex + STA_ATKS - 1];
4147 break;
4148 stringcase(lcm_cmdanim, rise):
4149 if(!(commandIndex < 11)) {
4150 if(commandIndex < MAX_ATKS - STA_ATKS + 1)
4151 commandIndex = MAX_ATKS - STA_ATKS + 1;
4152 (*ani_id) = dyn_anims.animrises[commandIndex + STA_ATKS - 1];
4154 break;
4155 stringcase(lcm_cmdanim, death):
4156 if(!(commandIndex < 11)) {
4157 if(commandIndex < MAX_ATKS - STA_ATKS + 1)
4158 commandIndex = MAX_ATKS - STA_ATKS + 1;
4159 (*ani_id) = dyn_anims.animdies[commandIndex + STA_ATKS - 1];
4161 break;
4162 stringcase(lcm_cmdanim, fall):
4163 if(commandIndex == 1)
4164 (*newanim)->bounce = 4;
4165 else
4166 if(!(commandIndex < 11)) {
4167 if(commandIndex < MAX_ATKS - STA_ATKS + 1)
4168 commandIndex = MAX_ATKS - STA_ATKS + 1;
4169 (*ani_id) = dyn_anims.animfalls[commandIndex + STA_ATKS - 1];
4171 break;
4172 stringcase(lcm_cmdanim, riseattack):
4173 if(!(commandIndex < 11)) {
4174 if(commandIndex < MAX_ATKS - STA_ATKS + 1)
4175 commandIndex = MAX_ATKS - STA_ATKS + 1;
4176 (*ani_id) = dyn_anims.animriseattacks[commandIndex + STA_ATKS - 1];
4178 break;
4179 stringcase(lcm_cmdanim, blockpain):
4180 if(!(commandIndex < 11)) {
4181 if(commandIndex < MAX_ATKS - STA_ATKS + 1)
4182 commandIndex = MAX_ATKS - STA_ATKS + 1;
4183 (*ani_id) = dyn_anims.animblkpains[commandIndex + STA_ATKS - 1];
4185 break;
4186 stringcase(lcm_cmdanim, freespecial):
4188 int flag;
4189 int ani;
4191 (*ani_id) = dyn_anims.animspecials[commandIndex - 1];
4192 switch (commandIndex) {
4193 case 1:
4194 flag = FLAG_FORWARD;
4195 ani = ANI_FREESPECIAL;
4196 goto set_flags;
4197 case 2:
4198 flag = FLAG_MOVEDOWN;
4199 ani = ANI_FREESPECIAL2;
4200 goto set_flags;
4201 case 3:
4202 flag = FLAG_MOVEUP;
4203 ani = ANI_FREESPECIAL3;
4204 set_flags:
4205 if(!is_set(newchar, ani)) {
4206 newchar->special[newchar->specials_loaded][0] = flag;
4207 newchar->special[newchar->specials_loaded][1] = flag;
4208 newchar->special[newchar->specials_loaded][2] = FLAG_ATTACK;
4209 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 2] = ani;
4210 newchar->special[newchar->specials_loaded][MAX_SPECIAL_INPUTS - 3] = 3;
4211 newchar->specials_loaded++;
4213 break;
4216 break;
4217 default:
4218 break;
4221 newchar->animation[(*ani_id)] = (*newanim);
4222 return 0;
4225 s_model *load_cached_model(char *name, char *owner, char unload) {
4226 s_model *newchar = NULL, *tempmodel = NULL;
4228 s_anim *newanim = NULL;
4230 char *filename = NULL,
4231 *buf = NULL, *scriptbuf = NULL, *command = NULL, *value = NULL, *value2 = NULL, *value3 = NULL;
4233 char namebuf[256] = { "" }, argbuf[MAX_ARG_LEN + 1] = "";
4235 ArgList arglist;
4237 float tempFloat;
4239 int ani_id = -1, script_id = -1, i = 0, j = 0, tempInt = 0, framecount = 0, frameset = 0, peek = 0, cacheindex = 0, curframe = 0, delay = 0, errorVal = 0, shadow_set = 0, idle = 0, move = 0, movez = 0, movea = 0, seta = -1, // Used for setting custom "a". Set to -1 to distinguish between disabled and setting "a" to 0
4240 frameshadow = -1, // -1 will use default shadow for this entity, otherwise will use this value
4241 soundtoplay = -1, aimoveset = 0, maskindex = -1;
4243 size_t size = 0;
4244 unsigned line = 1;
4245 size_t len = 0;
4247 ptrdiff_t pos = 0, index = 0;
4249 short bbox[5] = { 0, 0, 0, 0, 0 }, bbox_con[5] = {
4250 0, 0, 0, 0, 0}, abox[5] = {
4251 0, 0, 0, 0, 0}, offset[2] = {
4252 0, 0}, shadow_xz[2] = {
4253 0, 0}, shadow_coords[2] = {
4254 0, 0};
4256 float platform[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }, platform_con[8] = {
4257 0, 0, 0, 0, 0, 0, 0, 0};
4259 s_attack attack, *pattack = NULL;
4260 char *shutdownmessage = NULL;
4262 s_drawmethod drawmethod;
4264 unsigned char mapflag[MAX_COLOUR_MAPS]; // in 24bit mode, we need to know whether a colourmap is a common map or a palette
4266 static const char *pre_text = // this is the skeleton of frame function
4267 "void main()\n"
4268 "{\n" " int frame = getlocalvar(\"frame\");\n" " int animnum = getlocalvar(\"animnum\");\n" "\n}\n";
4270 static const char *sur_text = // end of function text
4271 "\n}\n";
4273 static const char *ifid_text = // if expression to check animation id
4274 " if(animnum==%d)\n" " {\n" " return;\n" " }\n";
4276 static const char *endifid_text = // end of if
4277 " return;\n" " }\n";
4279 static const char *if_text = // this is the if expression of frame function
4280 " if(frame==%d)\n" " {\n";
4282 static const char *endif_text = // end of if
4283 "\n" " }\n";
4285 static const char *comma_text = // arguments separator
4286 ", ";
4288 static const char *call_text = //begin of function call
4289 " %s(";
4291 static const char *endcall_text = //end of function call
4292 ");\n";
4294 modelCommands cmd;
4295 modelAttackCommands atk_cmd;
4296 s_scripts tempscripts;
4297 int *int_ptr;
4299 #ifdef DEBUG
4300 printf("load_cached_model: %s, unload: %d\n", name, unload);
4301 #endif
4303 // Model already loaded but we might want to unload after level is completed.
4304 if((tempmodel = findmodel(name)) != NULL) {
4305 update_model_loadflag(tempmodel, unload);
4306 return tempmodel;
4309 cacheindex = get_cached_model_index(name);
4310 if(cacheindex < 0)
4311 shutdown(1, "Fatal: No cache entry for '%s' within '%s'\n\n", name, owner);
4313 filename = model_cache[cacheindex].path;
4315 if(buffer_pakfile(filename, &buf, &size) != 1)
4316 shutdown(1, "Unable to open file '%s'\n\n", filename);
4318 scriptbuf = (char *) malloc(size * 2 + 1);
4320 if(scriptbuf == NULL) {
4321 shutdown(1, "Unable to create script buffer for file '%s' (%i bytes)", filename, size * 2);
4323 scriptbuf[0] = 0;
4325 //_peek_model_name(cacheindex);
4326 newchar = init_model(cacheindex, unload);
4327 //newchar->name = name;
4329 //attention, we increase models_loaded here, this can be dangerous if we access that value later on,
4330 //since recursive calls will change it!
4331 models_loaded++;
4332 addModel(newchar);
4334 attack = emptyattack; // empty attack
4335 drawmethod = plainmethod; // better than memset it to 0
4336 memset(mapflag, 0, MAX_COLOUR_MAPS);
4339 //char* test = "load knife 0";
4340 //ParseArgs(&arglist,test,argbuf);
4342 // Now interpret the contents of buf line by line
4343 while(pos < size) {
4344 //command = GET_ARG(0);
4345 if(ParseArgs(&arglist, buf + pos, argbuf)) {
4346 command = GET_ARG(0);
4347 cmd = getModelCommand(modelcmdlist, command);
4349 switch (cmd) {
4350 case CMD_MODEL_SUBCLASS:
4351 //inherit everything from an existing, cached model
4352 tempmodel = findmodel(GET_ARG(1));
4353 if(!tempmodel) {
4354 shutdownmessage =
4355 "tried to subclass a non-existing/not previously loaded model!";
4356 goto lCleanup;
4358 tempscripts = newchar->scripts;
4359 *newchar = *tempmodel;
4360 newchar->scripts = tempscripts;
4361 copy_all_scripts(&tempmodel->scripts, &newchar->scripts, 1);
4362 newchar->isSubclassed = 1;
4363 newchar->freetypes = MF_SCRIPTS;
4364 break;
4365 case CMD_MODEL_NAME:
4366 lcmHandleCommandName(&arglist, newchar, cacheindex);
4367 break;
4368 case CMD_MODEL_TYPE:
4369 lcmHandleCommandType(&arglist, newchar, filename);
4370 break;
4371 case CMD_MODEL_SUBTYPE:
4372 lcmHandleCommandSubtype(&arglist, newchar, filename);
4373 break;
4374 case CMD_MODEL_STATS:
4375 value = GET_ARG(1);
4376 newchar->stats[atoi(value)] = GET_FLOAT_ARG(2);
4377 break;
4378 case CMD_MODEL_SCROLL:
4379 value = GET_ARG(1);
4380 newchar->scroll = atof(value);
4381 break;
4382 case CMD_MODEL_MAKEINV: // Mar 12, 2005 - If a value is supplied, corresponds to amount of time the player spawns invincible
4383 newchar->makeinv = GET_INT_ARG(1) * GAME_SPEED;
4384 if(GET_INT_ARG(2))
4385 newchar->makeinv = -newchar->makeinv;
4386 break;
4387 case CMD_MODEL_RISEINV:
4388 newchar->riseinv = GET_INT_ARG(1) * GAME_SPEED;
4389 if(GET_INT_ARG(2))
4390 newchar->riseinv = -newchar->riseinv;
4391 break;
4392 case CMD_MODEL_LOAD:
4393 value = GET_ARG(1);
4394 tempmodel = findmodel(value);
4395 if(!tempmodel)
4396 load_cached_model(value, name, GET_INT_ARG(2));
4397 else
4398 update_model_loadflag(tempmodel, GET_INT_ARG(2));
4399 break;
4400 case CMD_MODEL_SCORE:
4401 newchar->score = GET_INT_ARG(1);
4402 newchar->multiple = GET_INT_ARG(2); // New var multiple for force/scoring
4403 break;
4404 case CMD_MODEL_SMARTBOMB:
4405 lcmHandleCommandSmartbomb(&arglist, newchar, filename);
4406 break;
4407 case CMD_MODEL_HITENEMY: // Flag to determine if an enemy projectile will hit enemies
4408 value = GET_ARG(1);
4409 if(atoi(value) == 1)
4410 newchar->candamage = newchar->hostile = TYPE_PLAYER | TYPE_ENEMY;
4411 else if(atoi(value) == 2)
4412 newchar->candamage = newchar->hostile = TYPE_PLAYER;
4413 newchar->ground = GET_INT_ARG(2); // Added to determine if enemies are damaged with mid air projectiles or ground only
4414 break;
4415 case CMD_MODEL_HOSTILE:
4416 lcmHandleCommandHostile(&arglist, newchar);
4417 break;
4418 case CMD_MODEL_CANDAMAGE:
4419 lcmHandleCommandCandamage(&arglist, newchar);
4420 break;
4421 case CMD_MODEL_PROJECTILEHIT:
4422 lcmHandleCommandProjectilehit(&arglist, newchar);
4423 break;
4424 case CMD_MODEL_AIMOVE:
4425 lcmHandleCommandAimove(&arglist, newchar, &aimoveset, filename);
4426 break;
4427 case CMD_MODEL_AIATTACK:
4428 if(newchar->aiattack == -1)
4429 newchar->aiattack = 0;
4430 //do nothing for now, until ai attack is implemented
4431 break;
4432 case CMD_MODEL_SUBJECT_TO_WALL:
4433 newchar->subject_to_wall = (0 != GET_INT_ARG(1));
4434 break;
4435 case CMD_MODEL_SUBJECT_TO_HOLE:
4436 newchar->subject_to_hole = (0 != GET_INT_ARG(1));
4437 break;
4438 case CMD_MODEL_SUBJECT_TO_PLATFORM:
4439 newchar->subject_to_platform = (0 != GET_INT_ARG(1));
4440 break;
4441 case CMD_MODEL_SUBJECT_TO_OBSTACLE:
4442 newchar->subject_to_obstacle = (0 != GET_INT_ARG(1));
4443 break;
4444 case CMD_MODEL_SUBJECT_TO_GRAVITY:
4445 newchar->subject_to_gravity = (0 != GET_INT_ARG(1));
4446 break;
4447 case CMD_MODEL_SUBJECT_TO_SCREEN:
4448 newchar->subject_to_screen = (0 != GET_INT_ARG(1));
4449 break;
4450 case CMD_MODEL_SUBJECT_TO_MINZ:
4451 newchar->subject_to_minz = (0 != GET_INT_ARG(1));
4452 break;
4453 case CMD_MODEL_SUBJECT_TO_MAXZ:
4454 newchar->subject_to_maxz = (0 != GET_INT_ARG(1));
4455 break;
4456 case CMD_MODEL_NO_ADJUST_BASE:
4457 newchar->no_adjust_base = (0 != GET_INT_ARG(1));
4458 break;
4459 // weapons
4460 case CMD_MODEL_WEAPLOSS:
4461 newchar->weaploss[0] = GET_INT_ARG(1);
4462 newchar->weaploss[1] = GET_INT_ARG(2);
4463 break;
4464 case CMD_MODEL_WEAPONS:
4465 lcmHandleCommandWeapons(&arglist, newchar);
4466 break;
4467 case CMD_MODEL_WEAPNUM: case CMD_MODEL_TYPESHOT: case CMD_MODEL_ANIMAL:
4468 case CMD_MODEL_INSTANTITEMDEATH: case CMD_MODEL_SECRET: case CMD_MODEL_MODELFLAG:
4469 case CMD_MODEL_BOUNCE: case CMD_MODEL_NOQUAKE: case CMD_MODEL_BLOCKBACK:
4470 case CMD_MODEL_NOLIFE: case CMD_MODEL_ANTIGRAB: case CMD_MODEL_GRABBACK:
4471 case CMD_MODEL_FALLDIE: case CMD_MODEL_DEATH: case CMD_MODEL_RISEATTACKTYPE:
4472 case CMD_MODEL_GRABFINISH: case CMD_MODEL_SHADOW: case CMD_MODEL_GFXSHADOW:
4473 case CMD_MODEL_AIRONLY: case CMD_MODEL_FMAP: case CMD_MODEL_TOFLIP:
4474 case CMD_MODEL_NODIEBLINK: case CMD_MODEL_HOLDBLOCK: case CMD_MODEL_BLOCKPAIN:
4475 case CMD_MODEL_NOPASSIVEBLOCK: case CMD_MODEL_PAINGRAB: case CMD_MODEL_GRABTURN:
4476 case CMD_MODEL_NODROP:
4477 switch(cmd) {
4478 case CMD_MODEL_WEAPNUM: value = &newchar->weapnum; break;
4479 case CMD_MODEL_TYPESHOT: value = &newchar->typeshot; break;
4480 case CMD_MODEL_ANIMAL: value = &newchar->animal; break;
4481 case CMD_MODEL_INSTANTITEMDEATH: value = &newchar->instantitemdeath; break;
4482 case CMD_MODEL_SECRET:value = &newchar->secret; break;
4483 case CMD_MODEL_MODELFLAG: //model copy flag
4484 value = &newchar->model_flag; break;
4485 case CMD_MODEL_BOUNCE: value = &newchar->bounce; break;
4486 case CMD_MODEL_NOQUAKE: // Mar 12, 2005 - Flag to determine if entity shakes screen
4487 value = &newchar->noquake; break;
4488 case CMD_MODEL_BLOCKBACK: // Flag to determine if attacks can be blocked from behind
4489 value = &newchar->blockback; break;
4490 case CMD_MODEL_NOLIFE: // Feb 25, 2005 - Flag to display enemy life or not
4491 value = &newchar->nolife; break;
4492 case CMD_MODEL_ANTIGRAB: // a can grab b: a->antigrab - b->grabforce <=0
4493 value = &newchar->antigrab; break;
4494 case CMD_MODEL_GRABBACK: value = &newchar->grabback; break;
4495 case CMD_MODEL_FALLDIE:
4496 case CMD_MODEL_DEATH: value = &newchar->falldie; break;
4497 case CMD_MODEL_RISEATTACKTYPE: value = &newchar->riseattacktype; break;
4498 case CMD_MODEL_GRABFINISH: value = &newchar->grabfinish; break;
4499 case CMD_MODEL_SHADOW: value = &newchar->shadow; break;
4500 case CMD_MODEL_GFXSHADOW: value = &newchar->gfxshadow; break;
4501 case CMD_MODEL_AIRONLY: // Shadows display in air only?
4502 value = &newchar->aironly; break;
4503 case CMD_MODEL_FMAP: // Map that corresponds with the remap when a character is frozen
4504 value = &newchar->fmap; break;
4505 case CMD_MODEL_TOFLIP: // Flag to determine if flashes images will be flipped or not
4506 value = &newchar->toflip; break;
4507 case CMD_MODEL_NODIEBLINK:
4508 // Added to determine if dying animation blinks or not
4509 value = &newchar->nodieblink; break;
4510 case CMD_MODEL_HOLDBLOCK: value = &newchar->holdblock; break;
4511 case CMD_MODEL_BLOCKPAIN: value = &newchar->blockpain; break;
4512 case CMD_MODEL_NOPASSIVEBLOCK: value = &newchar->nopassiveblock; break;
4513 case CMD_MODEL_PAINGRAB: value = &newchar->paingrab; break;
4514 case CMD_MODEL_GRABTURN: value = &newchar->grabturn; break;
4515 case CMD_MODEL_NODROP: value = &newchar->nodrop; break;
4516 default: value = NULL; break;
4519 *value = GET_INT_ARG(1);
4520 break;
4521 /* uns. char */
4522 case CMD_MODEL_SHOOTNUM: //here weapons things like shoot rest type of weapon ect..by tails
4523 newchar->shootnum = GET_INT_ARG(1);
4524 break;
4525 case CMD_MODEL_RELOAD:
4526 newchar->reload = GET_INT_ARG(1);
4527 break;
4529 /* short */
4530 case CMD_MODEL_THOLD:
4531 // Threshold for enemies/players block
4532 newchar->thold = GET_INT_ARG(1);
4533 break;
4534 case CMD_MODEL_THROWFRAMEWAIT:
4535 newchar->throwframewait = GET_INT_ARG(1);
4536 break;
4537 case CMD_MODEL_BLOCKODDS:
4538 // Odds that an attack will hit an enemy (1 : blockodds)
4539 newchar->blockodds = GET_INT_ARG(1);
4540 break;
4541 case CMD_MODEL_THROWDAMAGE:
4542 newchar->throwdamage = GET_INT_ARG(1);
4543 break;
4544 case CMD_MODEL_HEIGHT:
4545 newchar->height = GET_INT_ARG(1);
4546 break;
4547 case CMD_MODEL_COUNTER:
4548 newchar->counter = GET_INT_ARG(1);
4549 break;
4553 /* int */
4554 case CMD_MODEL_NOATFLASH: // Flag to determine if an opponents attack spawns their flash or not
4555 newchar->noatflash = GET_INT_ARG(1);
4556 break;
4557 case CMD_MODEL_SETLAYER:
4558 newchar->setlayer = GET_INT_ARG(1);
4559 break;
4560 case CMD_MODEL_GRABFORCE:
4561 newchar->grabforce = GET_INT_ARG(1);
4562 break;
4563 case CMD_MODEL_HEALTH:
4564 newchar->health = GET_INT_ARG(1);
4565 break;
4566 case CMD_MODEL_MP: //Left for backward compatability. See mpset. // mp values to put max mp for player by tails
4567 newchar->mp = GET_INT_ARG(1);
4568 break;
4572 case CMD_MODEL_OFFSCREENKILL:
4573 newchar->offscreenkill = GET_INT_ARG(1);
4574 break;
4579 case CMD_MODEL_RIDER: case CMD_MODEL_KNIFE: case CMD_MODEL_FIREB:
4580 case CMD_MODEL_PLAYSHOT: case CMD_MODEL_PLAYSHOTW: case CMD_MODEL_PLAYSHOTNO:
4581 case CMD_MODEL_STAR: case CMD_MODEL_BOMB: case CMD_MODEL_PLAYBOMB:
4582 case CMD_MODEL_FLASH: case CMD_MODEL_BFLASH: case CMD_MODEL_HITFLASH:
4583 case CMD_MODEL_BLOCKFLASH: case CMD_MODEL_PROJECT:
4584 switch(cmd) {
4585 case CMD_MODEL_PROJECT: // New projectile subtype
4586 int_ptr = &newchar->project; break;
4587 case CMD_MODEL_RIDER:
4588 int_ptr = &newchar->rider; break;
4589 case CMD_MODEL_KNIFE: case CMD_MODEL_FIREB:
4590 case CMD_MODEL_PLAYSHOT: case CMD_MODEL_PLAYSHOTW:
4591 int_ptr = &newchar->knife; break;
4592 case CMD_MODEL_PLAYSHOTNO:
4593 int_ptr = &newchar->pshotno; break;
4594 case CMD_MODEL_STAR:
4595 int_ptr = &newchar->star; break;
4596 case CMD_MODEL_BOMB: case CMD_MODEL_PLAYBOMB:
4597 int_ptr = &newchar->bomb; break;
4598 case CMD_MODEL_FLASH: // Now all characters can have their own flash - even projectiles (useful for blood)
4599 int_ptr = &newchar->flash; break;
4600 case CMD_MODEL_BFLASH: // Flash that is spawned if an attack is blocked
4601 int_ptr = &newchar->bflash; break;
4602 case CMD_MODEL_HITFLASH:
4603 int_ptr = &attack.hitflash; break;
4604 case CMD_MODEL_BLOCKFLASH:
4605 int_ptr = &attack.blockflash; break;
4606 default: int_ptr = NULL; break;
4608 lcmSetCachedModelIndexOrMinusOne(GET_ARG(1), int_ptr);
4609 break;
4610 case CMD_MODEL_DUST: // Spawned when hitting the ground to "kick up dust"
4611 lcmSetCachedModelIndexOrMinusOne(GET_ARG(1), &newchar->dust[0]);
4612 lcmSetCachedModelIndexOrMinusOne(GET_ARG(2), &newchar->dust[1]);
4613 lcmSetCachedModelIndexOrMinusOne(GET_ARG(3), &newchar->dust[2]);
4614 break;
4615 case CMD_MODEL_BRANCH: // for endlevel item's level branch
4616 value = GET_ARG(1);
4617 if(!newchar->branch) {
4618 newchar->branch = malloc(MAX_NAME_LEN + 1);
4619 newchar->branch[0] = 0;
4621 strncpy(newchar->branch, value, MAX_NAME_LEN);
4622 break;
4623 case CMD_MODEL_CANTGRAB:
4624 case CMD_MODEL_NOTGRAB:
4625 tempInt = GET_INT_ARG(1);
4626 if(tempInt == 2)
4627 newchar->grabforce = -999999;
4628 else
4629 newchar->antigrab = 1;
4630 break;
4631 case CMD_MODEL_SPEED:
4632 value = GET_ARG(1);
4633 newchar->speed = atof(value);
4634 newchar->speed /= 10;
4635 if(newchar->speed < 0.5)
4636 newchar->speed = 0.5;
4637 if(newchar->speed > 30)
4638 newchar->speed = 30;
4639 break;
4640 case CMD_MODEL_SPEEDF:
4641 value = GET_ARG(1);
4642 newchar->speed = atof(value);
4643 break;
4644 case CMD_MODEL_JUMPSPEED:
4645 value = GET_ARG(1);
4646 newchar->jumpspeed = atof(value);
4647 newchar->jumpspeed /= 10;
4648 break;
4649 case CMD_MODEL_JUMPSPEEDF:
4650 newchar->jumpspeed = GET_FLOAT_ARG(1);
4651 break;
4652 case CMD_MODEL_ANTIGRAVITY:
4653 newchar->antigravity = GET_FLOAT_ARG(1) / 100.f;
4654 break;
4655 case CMD_MODEL_STEALTH:
4656 newchar->stealth[0] = GET_INT_ARG(1);
4657 newchar->stealth[1] = GET_INT_ARG(2);
4658 break;
4659 case CMD_MODEL_JUGGLEPOINTS:
4660 newchar->jugglepoints[0] = GET_INT_ARG(1);
4661 newchar->jugglepoints[1] = GET_INT_ARG(1);
4662 break;
4663 case CMD_MODEL_GUARDPOINTS:
4664 newchar->guardpoints[0] = GET_INT_ARG(1);
4665 newchar->guardpoints[1] = GET_INT_ARG(1);
4666 break;
4667 case CMD_MODEL_DEFENSE:
4669 value = GET_ARG(1);
4670 atk_cmd = getModelAttackCommand(modelsattackcmdlist, value);
4671 if((int) atk_cmd >= 0) {
4672 newchar->defense_factors[atk_cmd] = GET_FLOAT_ARG(2);
4673 newchar->defense_pain[atk_cmd] = GET_FLOAT_ARG(3);
4674 newchar->defense_knockdown[atk_cmd] = GET_FLOAT_ARG(4);
4675 newchar->defense_blockpower[atk_cmd] = GET_FLOAT_ARG(5);
4676 newchar->defense_blockthreshold[atk_cmd] = GET_FLOAT_ARG(6);
4677 newchar->defense_blockratio[atk_cmd] = GET_FLOAT_ARG(7);
4678 newchar->defense_blocktype[atk_cmd] = GET_FLOAT_ARG(8);
4679 } else if(strnicmp(value, "normal", 6)==0) {
4680 tempInt = atoi(value+6);
4681 if(tempInt<11) tempInt = 11;
4682 newchar->defense_factors[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(2);
4683 newchar->defense_pain[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(3);
4684 newchar->defense_knockdown[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(4);
4685 newchar->defense_blockpower[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(5);
4686 newchar->defense_blockthreshold[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(6);
4687 newchar->defense_blockratio[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(7);
4688 newchar->defense_blocktype[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(8);
4689 } else if(stricmp(value, "ALL")==0) {
4690 for(i=0;i<dyn_anim_custom_maxvalues.max_attack_types;i++)
4692 newchar->defense_factors[i] = GET_FLOAT_ARG(2);
4693 newchar->defense_pain[i] = GET_FLOAT_ARG(3);
4694 newchar->defense_knockdown[i] = GET_FLOAT_ARG(4);
4695 newchar->defense_blockpower[i] = GET_FLOAT_ARG(5);
4696 newchar->defense_blockthreshold[i] = GET_FLOAT_ARG(6);
4697 newchar->defense_blockratio[i] = GET_FLOAT_ARG(7);
4698 newchar->defense_blocktype[i] = GET_FLOAT_ARG(8);
4702 break;
4703 case CMD_MODEL_OFFENSE:
4705 value = GET_ARG(1);
4706 atk_cmd = getModelAttackCommand(modelsattackcmdlist, value);
4707 if(atk_cmd >= 0) {
4708 newchar->offense_factors[atk_cmd] = GET_FLOAT_ARG(2);
4709 } else if(strnicmp(value, "normal", 6) == 0) {
4710 tempInt = atoi(value+6);
4711 if(tempInt<11) tempInt = 11;
4712 newchar->offense_factors[tempInt+STA_ATKS-1] = GET_FLOAT_ARG(2);
4713 } else if(stricmp(value, "ALL") == 0) {
4714 tempFloat = GET_FLOAT_ARG(2);
4715 for(i=0;i<dyn_anim_custom_maxvalues.max_attack_types;i++) {
4716 newchar->offense_factors[i] = tempFloat;
4720 break;
4721 case CMD_MODEL_JUMPHEIGHT:
4722 newchar->jumpheight = GET_FLOAT_ARG(1);
4723 break;
4724 case CMD_MODEL_JUMPMOVE:
4725 newchar->jumpmovex = GET_INT_ARG(1);
4726 newchar->jumpmovez = GET_INT_ARG(2);
4727 break;
4728 case CMD_MODEL_KNOCKDOWNCOUNT:
4729 newchar->knockdowncount = GET_FLOAT_ARG(1);
4730 break;
4731 case CMD_MODEL_GRABDISTANCE:
4732 newchar->grabdistance = GET_FLOAT_ARG(1); // 30-12-2004 and store for character
4733 break;
4734 case CMD_MODEL_KOMAP: // Remap when character is KO'd.
4735 newchar->komap[0] = GET_INT_ARG(1); //Remap.
4736 newchar->komap[1] = GET_INT_ARG(2); //Type: 0 start of fall/death, 1 last frame.
4737 break;
4738 case CMD_MODEL_HMAP: // Maps range unavailable to player in select screen.
4739 newchar->hmap1 = GET_INT_ARG(1); //First unavailable map.
4740 newchar->hmap2 = GET_INT_ARG(2); //Last unavailable map.
4741 break;
4742 case CMD_MODEL_NOMOVE:
4743 // If set, will be static (speed must be set to 0 or left blank)
4744 newchar->nomove = GET_INT_ARG(1);
4745 newchar->noflip = GET_INT_ARG(2); // If set, static will not flip directions
4746 if(newchar->nomove)
4747 newchar->nodrop = 1;
4748 break;
4749 case CMD_MODEL_RUNNING:
4750 // The speed at which the player runs
4751 newchar->runspeed = GET_FLOAT_ARG(1) / 10.f;
4752 newchar->runjumpheight = GET_FLOAT_ARG(2); // The height at which a player jumps when running
4753 newchar->runjumpdist = GET_FLOAT_ARG(3); // The distance a player jumps when running
4754 newchar->runupdown = GET_INT_ARG(4);
4755 newchar->runhold = GET_INT_ARG(5);
4756 break;
4757 case CMD_MODEL_EDELAY:
4758 newchar->edelay.mode = GET_INT_ARG(1);
4759 newchar->edelay.factor = GET_FLOAT_ARG(2);
4760 newchar->edelay.cap_min = GET_INT_ARG(3);
4761 newchar->edelay.cap_max = GET_INT_ARG(4);
4762 newchar->edelay.range_min = GET_INT_ARG(5);
4763 newchar->edelay.range_max = GET_INT_ARG(6);
4764 break;
4765 case CMD_MODEL_THROW:
4766 newchar->throwdist = GET_FLOAT_ARG(1);
4767 newchar->throwheight = GET_FLOAT_ARG(2);
4768 break;
4769 case CMD_MODEL_GRABWALK:
4770 newchar->grabwalkspeed = GET_FLOAT_ARG(1) / 10.f;
4771 if(newchar->grabwalkspeed < 0.5)
4772 newchar->grabwalkspeed = 0.5;
4773 break;
4774 case CMD_MODEL_DIESOUND:
4775 newchar->diesound = sound_load_sample(GET_ARG(1), packfile, 0);
4776 break;
4777 case CMD_MODEL_ICON:
4778 if(newchar->icon > -1) {
4779 shutdownmessage = "model has multiple icons defined";
4780 goto lCleanup;
4782 newchar->icon = loadsprite(GET_ARG(1), 0, 0, pixelformat); //use same palette as the owner
4783 newchar->iconpain = newchar->icon;
4784 newchar->icondie = newchar->icon;
4785 newchar->iconget = newchar->icon;
4786 break;
4787 case CMD_MODEL_ICONPAIN: case CMD_MODEL_ICONDIE: case CMD_MODEL_ICONGET:
4788 case CMD_MODEL_ICONW: case CMD_MODEL_ICONMPHIGH: case CMD_MODEL_ICONMPHALF:
4789 case CMD_MODEL_ICONMPLOW:
4790 switch(cmd) {
4791 case CMD_MODEL_ICONPAIN:
4792 int_ptr = &newchar->iconpain; break;
4793 case CMD_MODEL_ICONDIE:
4794 int_ptr = &newchar->icondie; break;
4795 case CMD_MODEL_ICONGET:
4796 int_ptr = &newchar->iconget; break;
4797 case CMD_MODEL_ICONW:
4798 int_ptr = &newchar->iconw; break;
4799 case CMD_MODEL_ICONMPHIGH:
4800 int_ptr = &newchar->iconmp[0]; break;
4801 case CMD_MODEL_ICONMPHALF:
4802 int_ptr = &newchar->iconmp[1]; break;
4803 case CMD_MODEL_ICONMPLOW:
4804 int_ptr = &newchar->iconmp[2]; break;
4805 default: int_ptr = NULL; break;
4807 *int_ptr = loadsprite(GET_ARG(1), 0, 0, pixelformat);
4808 break;
4809 case CMD_MODEL_PARROW:
4810 // Image that is displayed when player 1 spawns invincible
4811 newchar->parrow[0][0] = loadsprite(GET_ARG(1), 0, 0, pixelformat);
4812 newchar->parrow[0][1] = GET_INT_ARG(2);
4813 newchar->parrow[0][2] = GET_INT_ARG(3);
4814 break;
4815 case CMD_MODEL_PARROW2:
4816 // Image that is displayed when player 2 spawns invincible
4817 newchar->parrow[1][0] = loadsprite(GET_ARG(1), 0, 0, pixelformat);
4818 newchar->parrow[1][1] = GET_INT_ARG(2);
4819 newchar->parrow[1][2] = GET_INT_ARG(3);
4820 break;
4821 case CMD_MODEL_PARROW3:
4822 newchar->parrow[2][0] = loadsprite(GET_ARG(1), 0, 0, pixelformat);
4823 newchar->parrow[2][1] = GET_INT_ARG(2);
4824 newchar->parrow[2][2] = GET_INT_ARG(3);
4825 break;
4826 case CMD_MODEL_PARROW4:
4827 newchar->parrow[3][0] = loadsprite(GET_ARG(1), 0, 0, pixelformat);
4828 newchar->parrow[3][1] = GET_INT_ARG(2);
4829 newchar->parrow[3][2] = GET_INT_ARG(3);
4830 break;
4831 case CMD_MODEL_ATCHAIN:
4832 newchar->chainlength = 0;
4833 for(i = 0; i < MAX_ATCHAIN; i++) {
4834 newchar->atchain[i] = GET_INT_ARG(i + 1);
4835 if(newchar->atchain[i] < 0)
4836 newchar->atchain[i] = 0;
4837 if(newchar->atchain[i] > dyn_anim_custom_maxvalues.max_attacks)
4838 newchar->atchain[i] = dyn_anim_custom_maxvalues.max_attacks;
4839 if(newchar->atchain[i])
4840 newchar->chainlength = i + 1;
4842 break;
4843 case CMD_MODEL_COMBOSTYLE:
4844 newchar->combostyle = GET_INT_ARG(1);
4845 break;
4846 case CMD_MODEL_CREDIT:
4847 newchar->credit = GET_INT_ARG(1);
4848 break;
4849 case CMD_MODEL_NOPAIN:
4850 newchar->nopain = GET_INT_ARG(1);
4851 break;
4852 case CMD_MODEL_ESCAPEHITS:
4853 // How many times an enemy can be hit before retaliating
4854 newchar->escapehits = GET_INT_ARG(1);
4855 break;
4856 case CMD_MODEL_CHARGERATE:
4857 // How much mp does this character gain while recharging?
4858 newchar->chargerate = GET_INT_ARG(1);
4859 break;
4860 case CMD_MODEL_MPRATE:
4861 newchar->mprate = GET_INT_ARG(1);
4862 break;
4863 case CMD_MODEL_MPSET:
4864 // Mp bar wax/wane.
4865 newchar->mp = GET_INT_ARG(1); //Max MP.
4866 newchar->mpstable = GET_INT_ARG(2); //MP stable setting.
4867 newchar->mpstableval = GET_INT_ARG(3); //MP stable value (% Mp bar will try and maintain).
4868 newchar->mprate = GET_INT_ARG(4); //Rate MP value rises over time.
4869 newchar->mpdroprate = GET_INT_ARG(5); //Rate MP value drops over time.
4870 newchar->chargerate = GET_INT_ARG(6); //MP Chargerate.
4871 break;
4872 case CMD_MODEL_SLEEPWAIT:
4873 newchar->sleepwait = GET_INT_ARG(1);
4874 break;
4875 case CMD_MODEL_GUARDRATE:
4876 newchar->guardrate = GET_INT_ARG(1);
4877 break;
4878 case CMD_MODEL_AGGRESSION:
4879 newchar->aggression = GET_INT_ARG(1);
4880 break;
4881 case CMD_MODEL_RISETIME:
4882 newchar->risetime[0] = GET_INT_ARG(1);
4883 newchar->risetime[1] = GET_INT_ARG(2);
4884 break;
4885 case CMD_MODEL_FACING:
4886 newchar->facing = GET_INT_ARG(1);
4887 break;
4888 case CMD_MODEL_TURNDELAY:
4889 newchar->turndelay = GET_INT_ARG(1);
4890 break;
4891 case CMD_MODEL_LIFESPAN:
4892 newchar->lifespan = GET_FLOAT_ARG(1) * GAME_SPEED;
4893 break;
4894 case CMD_MODEL_SUMMONKILL:
4895 newchar->summonkill = GET_INT_ARG(1);
4896 break;
4897 case CMD_MODEL_LIFEPOSITION:
4898 if((value = GET_ARG(1))[0])
4899 newchar->hpx = atoi(value);
4900 if((value = GET_ARG(2))[0])
4901 newchar->hpy = atoi(value);
4902 break;
4903 case CMD_MODEL_LIFEBARSTATUS:
4904 _readbarstatus(buf + pos, &(newchar->hpbarstatus));
4905 newchar->hpbarstatus.colourtable = &color_tables.hp;
4906 break;
4907 case CMD_MODEL_ICONPOSITION:
4908 if((value = GET_ARG(1))[0])
4909 newchar->iconx = atoi(value);
4910 if((value = GET_ARG(2))[0])
4911 newchar->icony = atoi(value);
4912 break;
4913 case CMD_MODEL_NAMEPOSITION:
4914 if((value = GET_ARG(1))[0])
4915 newchar->namex = atoi(value);
4916 if((value = GET_ARG(2))[0])
4917 newchar->namey = atoi(value);
4918 break;
4919 case CMD_MODEL_COM:
4920 tempInt = lcmHandleCommandCom(&arglist, newchar, &value, &shutdownmessage);
4921 if(tempInt == 0) ;
4922 else if(tempInt == 1) {
4923 printf("WARNING: Invalid freespecial command '%s' in '%s', line %u\n", value, filename, line);
4924 goto next_line;
4925 } else
4926 goto lCleanup;
4927 // End section for custom freespecials
4928 break;
4929 case CMD_MODEL_REMAP:
4931 // This command should not be used under 24bit mode, but for old mods, just give it a default palette
4932 value = GET_ARG(1);
4933 value2 = GET_ARG(2);
4934 errorVal = load_colourmap(newchar, value, value2);
4935 if(pixelformat == PIXEL_x8 && newchar->palette == NULL) {
4936 newchar->palette = malloc(PAL_BYTES);
4937 memcpy(newchar->palette, pal, PAL_BYTES);
4939 mapflag[newchar->maps_loaded - 1] = 1;
4940 if(!errorVal) {
4941 switch (errorVal) {
4942 case 0: // uhm wait, we just tested for !errorVal...
4943 shutdownmessage =
4944 "Failed to create colourmap. Image Used Twice!";
4945 goto lCleanup;
4946 break;
4947 case -1:
4948 shutdownmessage =
4949 "Failed to create colourmap. MAX_COLOUR_MAPS full!";
4950 goto lCleanup;
4951 break;
4952 case -2:
4953 shutdownmessage =
4954 "Failed to create colourmap. Failed to tracemalloc(256)!";
4955 goto lCleanup;
4956 break;
4957 case -3:
4958 shutdownmessage =
4959 "Failed to create colourmap. Failed to create bitmap1";
4960 goto lCleanup;
4961 break;
4962 case -4:
4963 shutdownmessage =
4964 "Failed to create colourmap. Failed to create bitmap2";
4965 goto lCleanup;
4966 break;
4970 break;
4971 case CMD_MODEL_PALETTE:
4972 // main palette for the entity under 24bit mode
4973 if(pixelformat != PIXEL_x8)
4974 printf("Warning: command '%s' is not available under 8bit mode\n",
4975 command);
4976 else if(newchar->palette == NULL) {
4977 value = GET_ARG(1);
4978 newchar->palette = malloc(PAL_BYTES);
4979 if(loadimagepalette(value, packfile, newchar->palette) == 0) {
4980 shutdownmessage = "Failed to load palette!";
4981 goto lCleanup;
4984 break;
4985 case CMD_MODEL_ALTERNATEPAL:
4986 // remap for the entity under 24bit mode, this method can replace remap command
4987 if(pixelformat != PIXEL_x8)
4988 printf("Warning: command '%s' is not available under 8bit mode\n",
4989 command);
4990 else if(newchar->maps_loaded < MAX_COLOUR_MAPS) {
4991 value = GET_ARG(1);
4992 newchar->colourmap[(int) newchar->maps_loaded] = malloc(PAL_BYTES);
4993 if(loadimagepalette
4994 (value, packfile,
4995 newchar->colourmap[(int) newchar->maps_loaded]) == 0) {
4996 shutdownmessage = "Failed to load palette!";
4997 goto lCleanup;
4999 newchar->maps_loaded++;
5001 break;
5002 case CMD_MODEL_GLOBALMAP:
5003 // use global palette under 24bit mode, so some entity/panel/bg can still use palette feature, that saves some memory
5004 if(pixelformat != PIXEL_x8)
5005 printf("Warning: command '%s' is not available under 8bit mode\n",
5006 command);
5007 else
5008 newchar->globalmap = GET_INT_ARG(1);
5009 break;
5010 case CMD_MODEL_ALPHA:
5011 newchar->alpha = GET_INT_ARG(1);
5012 break;
5013 case CMD_MODEL_REMOVE:
5014 newchar->remove = GET_INT_ARG(1);
5015 break;
5016 case CMD_MODEL_SCRIPT:
5017 //load the update script
5018 lcmHandleCommandScripts(&arglist, newchar->scripts.update_script,
5019 "updateentityscript", filename);
5020 break;
5021 case CMD_MODEL_THINKSCRIPT:
5022 lcmHandleCommandScripts(&arglist, newchar->scripts.think_script, "thinkscript",
5023 filename);
5024 break;
5025 case CMD_MODEL_TAKEDAMAGESCRIPT:
5026 lcmHandleCommandScripts(&arglist, newchar->scripts.takedamage_script,
5027 "takedamagescript", filename);
5028 break;
5029 case CMD_MODEL_ONFALLSCRIPT:
5030 lcmHandleCommandScripts(&arglist, newchar->scripts.onfall_script,
5031 "onfallscript", filename);
5032 break;
5033 case CMD_MODEL_ONPAINSCRIPT:
5034 lcmHandleCommandScripts(&arglist, newchar->scripts.onpain_script,
5035 "onpainscript", filename);
5036 break;
5037 case CMD_MODEL_ONBLOCKSSCRIPT:
5038 lcmHandleCommandScripts(&arglist, newchar->scripts.onblocks_script,
5039 "onblocksscript", filename);
5040 break;
5041 case CMD_MODEL_ONBLOCKWSCRIPT:
5042 lcmHandleCommandScripts(&arglist, newchar->scripts.onblockw_script,
5043 "onblockwscript", filename);
5044 break;
5045 case CMD_MODEL_ONBLOCKOSCRIPT:
5046 lcmHandleCommandScripts(&arglist, newchar->scripts.onblocko_script,
5047 "onblockoscript", filename);
5048 break;
5049 case CMD_MODEL_ONBLOCKZSCRIPT:
5050 lcmHandleCommandScripts(&arglist, newchar->scripts.onblockz_script,
5051 "onblockzscript", filename);
5052 break;
5053 case CMD_MODEL_ONBLOCKASCRIPT:
5054 lcmHandleCommandScripts(&arglist, newchar->scripts.onblocka_script,
5055 "onblockascript", filename);
5056 break;
5057 case CMD_MODEL_ONMOVEXSCRIPT:
5058 lcmHandleCommandScripts(&arglist, newchar->scripts.onmovex_script,
5059 "onmovexscript", filename);
5060 break;
5061 case CMD_MODEL_ONMOVEZSCRIPT:
5062 lcmHandleCommandScripts(&arglist, newchar->scripts.onmovez_script,
5063 "onmovezscript", filename);
5064 break;
5065 case CMD_MODEL_ONMOVEASCRIPT:
5066 lcmHandleCommandScripts(&arglist, newchar->scripts.onmovea_script,
5067 "onmoveascript", filename);
5068 break;
5069 case CMD_MODEL_ONDEATHSCRIPT:
5070 lcmHandleCommandScripts(&arglist, newchar->scripts.ondeath_script,
5071 "ondeathscript", filename);
5072 break;
5073 case CMD_MODEL_ONKILLSCRIPT:
5074 lcmHandleCommandScripts(&arglist, newchar->scripts.onkill_script,
5075 "onkillscript", filename);
5076 break;
5077 case CMD_MODEL_DIDBLOCKSCRIPT:
5078 lcmHandleCommandScripts(&arglist, newchar->scripts.didblock_script,
5079 "didblockscript", filename);
5080 break;
5081 case CMD_MODEL_ONDOATTACKSCRIPT:
5082 lcmHandleCommandScripts(&arglist, newchar->scripts.ondoattack_script,
5083 "ondoattackscript", filename);
5084 break;
5085 case CMD_MODEL_DIDHITSCRIPT:
5086 lcmHandleCommandScripts(&arglist, newchar->scripts.didhit_script,
5087 "didhitscript", filename);
5088 break;
5089 case CMD_MODEL_ONSPAWNSCRIPT:
5090 lcmHandleCommandScripts(&arglist, newchar->scripts.onspawn_script,
5091 "onspawnscript", filename);
5092 break;
5093 case CMD_MODEL_ANIMATIONSCRIPT:
5094 Script_Init(newchar->scripts.animation_script, "animationscript", 0);
5095 if(!load_script(newchar->scripts.animation_script, GET_ARG(1))) {
5096 shutdownmessage = "Unable to load animation script!";
5097 goto lCleanup;
5099 //dont compile, until at end of this function
5100 break;
5101 case CMD_MODEL_KEYSCRIPT:
5102 lcmHandleCommandScripts(&arglist, newchar->scripts.key_script,
5103 "entitykeyscript", filename);
5104 break;
5105 case CMD_MODEL_ANIM:
5106 frameset = 0;
5107 framecount = 0;
5108 curframe = 0;
5109 idle = 0;
5110 memset(bbox, 0, sizeof(bbox));
5111 memset(abox, 0, sizeof(abox));
5112 memset(offset, 0, sizeof(offset));
5113 memset(shadow_coords, 0, sizeof(shadow_coords));
5114 memset(shadow_xz, 0, sizeof(shadow_xz));
5115 memset(platform, 0, sizeof(platform));
5116 shadow_set = 0;
5117 attack = emptyattack;
5118 attack.hitsound = -1;
5119 attack.hitflash = -1;
5120 attack.blockflash = -1;
5121 attack.blocksound = -1;
5122 drawmethod = plainmethod;
5123 move = 0;
5124 movez = 0;
5125 movea = 0;
5126 seta = -1;
5127 frameshadow = -1;
5128 soundtoplay = -1;
5130 tempInt = lcmHandleCommandAnim(&arglist, newchar, &newanim, &ani_id, &value, &shutdownmessage, &attack);
5131 if(tempInt == 0) ;
5132 else if (tempInt == 1) {
5133 printf("WARNING: invalid animation name '%s', file '%s', line %u\n", value, filename, line);
5134 goto next_line;
5135 } else
5136 goto lCleanup;
5138 break;
5139 case CMD_MODEL_LOOP:
5140 if(!newanim) {
5141 shutdownmessage = "Can't set loop: no animation specified!";
5142 goto lCleanup;
5144 newanim->loop[0] = GET_INT_ARG(1); //0 = Off, 1 = on.
5145 newanim->loop[1] = GET_INT_ARG(2); //Loop to frame.
5146 newanim->loop[2] = GET_INT_ARG(3); //Loop end frame.
5147 break;
5148 case CMD_MODEL_ANIMHEIGHT:
5149 newanim->height = GET_INT_ARG(1);
5150 break;
5151 case CMD_MODEL_DELAY:
5152 delay = GET_INT_ARG(1);
5153 break;
5154 case CMD_MODEL_OFFSET:
5155 offset[0] = GET_INT_ARG(1);
5156 offset[1] = GET_INT_ARG(2);
5157 break;
5158 case CMD_MODEL_SHADOWCOORDS:
5159 shadow_xz[0] = GET_INT_ARG(1);
5160 shadow_xz[1] = GET_INT_ARG(2);
5161 shadow_set = 1;
5162 break;
5163 case CMD_MODEL_ENERGYCOST:
5164 case CMD_MODEL_MPCOST:
5165 newanim->energycost[0] = GET_INT_ARG(1);
5166 newanim->energycost[1] = GET_INT_ARG(2);
5167 newanim->energycost[2] = GET_INT_ARG(3);
5168 break;
5169 case CMD_MODEL_MPONLY:
5170 newanim->energycost[1] = GET_INT_ARG(1);
5171 break;
5172 case CMD_MODEL_CHARGETIME:
5173 newanim->chargetime = GET_FLOAT_ARG(1);
5174 break;
5175 case CMD_MODEL_DIVE: //dive kicks
5176 newanim->dive[0] = GET_FLOAT_ARG(1);
5177 newanim->dive[1] = GET_FLOAT_ARG(2);
5178 break;
5179 case CMD_MODEL_DIVE1:
5180 newanim->dive[0] = GET_FLOAT_ARG(1);
5181 break;
5182 case CMD_MODEL_DIVE2:
5183 newanim->dive[1] = GET_FLOAT_ARG(1);
5184 break;
5185 case CMD_MODEL_ATTACKONE:
5186 newanim->attackone = GET_INT_ARG(1);
5187 break;
5188 case CMD_MODEL_COUNTERATTACK:
5189 attack.counterattack = GET_INT_ARG(1);
5190 break;
5191 case CMD_MODEL_THROWFRAME:
5192 case CMD_MODEL_PSHOTFRAME:
5193 case CMD_MODEL_PSHOTFRAMEW:
5194 case CMD_MODEL_PSHOTFRAMENO:
5195 newanim->throwframe = GET_INT_ARG(1);
5196 newanim->throwa = GET_INT_ARG(2);
5197 if(!newanim->throwa)
5198 newanim->throwa = 70;
5199 else if(newanim->throwa == -1)
5200 newanim->throwa = 0;
5201 break;
5202 case CMD_MODEL_SHOOTFRAME:
5203 newanim->shootframe = GET_INT_ARG(1);
5204 newanim->throwa = GET_INT_ARG(2);
5205 if(newanim->throwa == -1)
5206 newanim->throwa = 0;
5207 break;
5208 case CMD_MODEL_TOSSFRAME:
5209 case CMD_MODEL_PBOMBFRAME:
5210 newanim->tossframe = GET_INT_ARG(1);
5211 newanim->throwa = GET_INT_ARG(2);
5212 if(newanim->throwa < 0)
5213 newanim->throwa = -1;
5214 break;
5215 case CMD_MODEL_CUSTKNIFE:
5216 case CMD_MODEL_CUSTPSHOT:
5217 case CMD_MODEL_CUSTPSHOTW:
5218 newanim->custknife = get_cached_model_index(GET_ARG(1));
5219 break;
5220 case CMD_MODEL_CUSTPSHOTNO:
5221 newanim->custpshotno = get_cached_model_index(GET_ARG(1));
5222 break;
5223 case CMD_MODEL_CUSTBOMB:
5224 case CMD_MODEL_CUSTPBOMB:
5225 newanim->custbomb = get_cached_model_index(GET_ARG(1));
5226 break;
5227 case CMD_MODEL_CUSTSTAR:
5228 newanim->custstar = get_cached_model_index(GET_ARG(1));
5229 break;
5230 case CMD_MODEL_JUMPFRAME:
5232 newanim->jumpframe = GET_INT_ARG(1);
5233 newanim->jumpv = GET_FLOAT_ARG(2); // Added so movement can be customized for jumpframes
5234 value = GET_ARG(3);
5235 if(value[0]) {
5236 newanim->jumpx = GET_FLOAT_ARG(3);
5237 newanim->jumpz = GET_FLOAT_ARG(4);
5238 } else // k, only for backward compatibility :((((((((((((((((
5240 if(newanim->jumpv <= 0) {
5241 if(newchar->type == TYPE_PLAYER) {
5242 newanim->jumpv = newchar->jumpheight / 2;
5243 newanim->jumpz = 0;
5244 newanim->jumpx = 2;
5245 } else {
5246 newanim->jumpv = newchar->jumpheight;
5247 newanim->jumpz = newanim->jumpx = 0;
5249 } else {
5250 if(newchar->type != TYPE_ENEMY
5251 && newchar->type != TYPE_NPC)
5252 newanim->jumpz = newanim->jumpx = 0;
5253 else {
5254 newanim->jumpz = 0;
5255 newanim->jumpx = (float) 1.3;
5260 value = GET_ARG(5);
5261 if(value[0])
5262 newanim->jumpd = get_cached_model_index(value);
5263 else
5264 newanim->jumpd = -1;
5267 break;
5268 case CMD_MODEL_BOUNCEFACTOR:
5269 newanim->bounce = GET_FLOAT_ARG(1);
5270 break;
5271 case CMD_MODEL_LANDFRAME:
5272 newanim->landframe[0] = GET_INT_ARG(1);
5273 value = GET_ARG(2);
5274 if(value[0])
5275 newanim->landframe[1] = get_cached_model_index(value);
5276 else
5277 newanim->landframe[1] = -1;
5278 break;
5279 case CMD_MODEL_DROPFRAME:
5280 newanim->dropframe = GET_INT_ARG(1);
5281 break;
5282 case CMD_MODEL_CANCEL:
5283 if(lcmHandleCommandCancel(&arglist, newchar, newanim, &value, ani_id, &shutdownmessage, filename, command) == -1) goto lCleanup;
5284 break;
5285 case CMD_MODEL_SOUND:
5286 soundtoplay = sound_load_sample(GET_ARG(1), packfile, 0);
5287 break;
5288 case CMD_MODEL_HITFX:
5289 attack.hitsound = sound_load_sample(GET_ARG(1), packfile, 0);
5290 break;
5291 case CMD_MODEL_BLOCKFX:
5292 attack.blocksound = sound_load_sample(GET_ARG(1), packfile, 0);
5293 break;
5294 case CMD_MODEL_FASTATTACK:
5295 newanim->fastattack = GET_INT_ARG(1);
5296 break;
5297 case CMD_MODEL_BBOX:
5298 bbox[0] = GET_INT_ARG(1);
5299 bbox[1] = GET_INT_ARG(2);
5300 bbox[2] = GET_INT_ARG(3);
5301 bbox[3] = GET_INT_ARG(4);
5302 bbox[4] = GET_INT_ARG(5);
5303 break;
5304 case CMD_MODEL_BBOXZ:
5305 bbox[4] = GET_INT_ARG(1);
5306 break;
5307 case CMD_MODEL_PLATFORM:
5308 //for(i=0;(GET_ARG(i+1)[0]; i++);
5309 for(i = 0; i < arglist.count && arglist.args[i] && arglist.args[i][0]; i++) ;
5310 if(i < 8) {
5311 for(i = 0; i < 6; i++)
5312 platform[i + 2] = GET_FLOAT_ARG(i + 1);
5313 platform[0] = 99999;
5314 } else
5315 for(i = 0; i < 8; i++)
5316 platform[i] = GET_FLOAT_ARG(i + 1);
5317 break;
5318 case CMD_MODEL_DRAWMETHOD:
5319 // special effects
5320 drawmethod.scalex = GET_INT_ARG(1);
5321 drawmethod.scaley = GET_INT_ARG(2);
5322 drawmethod.flipx = GET_INT_ARG(3);
5323 drawmethod.flipy = GET_INT_ARG(4);
5324 drawmethod.shiftx = GET_INT_ARG(5);
5325 drawmethod.alpha = GET_INT_ARG(6);
5326 if(!blendfx_is_set) {
5327 if(drawmethod.alpha > 0 && drawmethod.alpha <= MAX_BLENDINGS) {
5328 blendfx[drawmethod.alpha - 1] = 1;
5331 drawmethod.remap = GET_INT_ARG(7);
5332 drawmethod.fillcolor = parsecolor(GET_ARG(8));
5333 drawmethod.rotate = GET_INT_ARG(9);
5334 drawmethod.fliprotate = GET_INT_ARG(10) % 360;
5335 if(drawmethod.scalex < 0) {
5336 drawmethod.scalex = -drawmethod.scalex;
5337 drawmethod.flipx = !drawmethod.flipx;
5339 if(drawmethod.scaley < 0) {
5340 drawmethod.scaley = -drawmethod.scaley;
5341 drawmethod.flipy = !drawmethod.flipy;
5343 if(drawmethod.rotate) {
5344 if(drawmethod.rotate < 0)
5345 drawmethod.rotate += 360;
5347 drawmethod.flag = 1;
5348 break;
5349 case CMD_MODEL_NODRAWMETHOD:
5350 //disable special effects
5351 drawmethod.flag = 0;
5352 break;
5353 case CMD_MODEL_ATTACK:
5354 case CMD_MODEL_ATTACK1:
5355 case CMD_MODEL_ATTACK2:
5356 case CMD_MODEL_ATTACK3:
5357 case CMD_MODEL_ATTACK4:
5358 case CMD_MODEL_ATTACK5:
5359 case CMD_MODEL_ATTACK6:
5360 case CMD_MODEL_ATTACK7:
5361 case CMD_MODEL_ATTACK8:
5362 case CMD_MODEL_ATTACK9:
5363 case CMD_MODEL_ATTACK10:
5364 case CMD_MODEL_ATTACK11:
5365 case CMD_MODEL_ATTACK12:
5366 case CMD_MODEL_ATTACK13:
5367 case CMD_MODEL_ATTACK14:
5368 case CMD_MODEL_ATTACK15:
5369 case CMD_MODEL_ATTACK16:
5370 case CMD_MODEL_ATTACK17:
5371 case CMD_MODEL_ATTACK18:
5372 case CMD_MODEL_ATTACK19:
5373 case CMD_MODEL_ATTACK20:
5374 case CMD_MODEL_SHOCK:
5375 case CMD_MODEL_BURN:
5376 case CMD_MODEL_STEAL:
5377 case CMD_MODEL_FREEZE:
5378 case CMD_MODEL_ITEMBOX:
5379 abox[0] = GET_INT_ARG(1);
5380 abox[1] = GET_INT_ARG(2);
5381 abox[2] = GET_INT_ARG(3);
5382 abox[3] = GET_INT_ARG(4);
5383 attack.dropv[0] = 3;
5384 attack.dropv[1] = (float) 1.2;
5385 attack.dropv[2] = 0;
5386 attack.attack_force = GET_INT_ARG(5);
5388 attack.attack_drop = GET_INT_ARG(6);
5390 attack.no_block = GET_INT_ARG(7);
5391 attack.no_flash = GET_INT_ARG(8);
5392 attack.pause_add = GET_INT_ARG(9);
5393 attack.attack_coords[4] = GET_INT_ARG(10); // depth or z
5395 switch (cmd) {
5396 case CMD_MODEL_ATTACK:
5397 case CMD_MODEL_ATTACK1:
5398 attack.attack_type = ATK_NORMAL;
5399 break;
5400 case CMD_MODEL_ATTACK2:
5401 attack.attack_type = ATK_NORMAL2;
5402 break;
5403 case CMD_MODEL_ATTACK3:
5404 attack.attack_type = ATK_NORMAL3;
5405 break;
5406 case CMD_MODEL_ATTACK4:
5407 attack.attack_type = ATK_NORMAL4;
5408 break;
5409 case CMD_MODEL_ATTACK5:
5410 attack.attack_type = ATK_NORMAL5;
5411 break;
5412 case CMD_MODEL_ATTACK6:
5413 attack.attack_type = ATK_NORMAL6;
5414 break;
5415 case CMD_MODEL_ATTACK7:
5416 attack.attack_type = ATK_NORMAL7;
5417 break;
5418 case CMD_MODEL_ATTACK8:
5419 attack.attack_type = ATK_NORMAL8;
5420 break;
5421 case CMD_MODEL_ATTACK9:
5422 attack.attack_type = ATK_NORMAL9;
5423 break;
5424 case CMD_MODEL_ATTACK10:
5425 attack.attack_type = ATK_NORMAL10;
5426 break;
5427 case CMD_MODEL_SHOCK:
5428 attack.attack_type = ATK_SHOCK;
5429 break;
5430 case CMD_MODEL_BURN:
5431 attack.attack_type = ATK_BURN;
5432 break;
5433 case CMD_MODEL_STEAL:
5434 attack.steal = 1;
5435 attack.attack_type = ATK_STEAL;
5436 break;
5437 case CMD_MODEL_FREEZE:
5438 attack.attack_type = ATK_FREEZE;
5439 attack.freeze = 1;
5440 attack.freezetime = GET_INT_ARG(6) * GAME_SPEED;
5441 attack.forcemap = -1;
5442 attack.attack_drop = 0;
5443 break;
5444 case CMD_MODEL_ITEMBOX:
5445 attack.attack_type = ATK_ITEM;
5446 break;
5447 default:
5448 tempInt = atoi(command + 6);
5449 if(tempInt < MAX_ATKS - STA_ATKS + 1)
5450 tempInt = MAX_ATKS - STA_ATKS + 1;
5451 attack.attack_type = tempInt + STA_ATKS - 1;
5453 break;
5454 case CMD_MODEL_ATTACKZ:
5455 case CMD_MODEL_HITZ:
5456 attack.attack_coords[4] = GET_INT_ARG(1);
5457 break;
5458 case CMD_MODEL_BLAST:
5459 abox[0] = GET_INT_ARG(1);
5460 abox[1] = GET_INT_ARG(2);
5461 abox[2] = GET_INT_ARG(3);
5462 abox[3] = GET_INT_ARG(4);
5463 attack.dropv[0] = 3;
5464 attack.dropv[1] = 2.5;
5465 attack.dropv[2] = 0;
5466 attack.attack_force = GET_INT_ARG(5);
5467 attack.no_block = GET_INT_ARG(6);
5468 attack.no_flash = GET_INT_ARG(7);
5469 attack.pause_add = GET_INT_ARG(8);
5470 attack.attack_drop = 1;
5471 attack.attack_type = ATK_BLAST;
5472 attack.attack_coords[4] = GET_INT_ARG(9); // depth or z
5473 attack.blast = 1;
5474 break;
5475 case CMD_MODEL_DROPV:
5476 // drop velocity add if the target is knocked down
5477 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5478 pattack->dropv[0] = GET_FLOAT_ARG(1); // height add
5479 pattack->dropv[1] = GET_FLOAT_ARG(2); // xdir add
5480 pattack->dropv[2] = GET_FLOAT_ARG(3); // zdir add
5481 break;
5482 case CMD_MODEL_OTG:
5483 // Over The Ground hit.
5484 attack.otg = GET_INT_ARG(1);
5485 break;
5486 case CMD_MODEL_JUGGLECOST:
5487 // if cost >= opponents jugglepoints , we can juggle
5488 attack.jugglecost = GET_INT_ARG(1);
5489 break;
5490 case CMD_MODEL_GUARDCOST:
5491 // if cost >= opponents guardpoints , opponent will play guardcrush anim
5492 attack.guardcost = GET_INT_ARG(1);
5493 break;
5494 case CMD_MODEL_STUN:
5495 //Like Freeze, but no auto remap.
5496 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5497 pattack->freeze = 1;
5498 pattack->freezetime = GET_INT_ARG(1) * GAME_SPEED;
5499 pattack->attack_drop = 0;
5500 break;
5501 case CMD_MODEL_GRABIN:
5502 // fake grab distanse efffect, not link
5503 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5504 pattack->grab = GET_INT_ARG(1);
5505 pattack->grab_distance = GET_FLOAT_ARG(2);
5506 break;
5507 case CMD_MODEL_NOREFLECT:
5508 // only cost target's hp, don't knock down or cause pain, unless the target is killed
5509 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5510 pattack->no_pain = GET_INT_ARG(1);
5511 break;
5512 case CMD_MODEL_FORCEDIRECTION:
5513 // the attack direction
5514 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5515 pattack->force_direction = GET_INT_ARG(1);
5516 break;
5517 case CMD_MODEL_DAMAGEONLANDING:
5518 // fake throw damage on landing
5519 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5520 pattack->damage_on_landing = GET_INT_ARG(1);
5521 pattack->blast = GET_INT_ARG(2);
5522 break;
5523 case CMD_MODEL_SEAL:
5524 // Disable special moves for specified time.
5525 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5526 pattack->sealtime = GET_INT_ARG(1) * GAME_SPEED;
5527 pattack->seal = GET_INT_ARG(2);
5528 break;
5529 case CMD_MODEL_STAYDOWN:
5530 // Disable special moves for specified time.
5531 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5532 pattack->staydown[0] = GET_INT_ARG(1); //Risetime modifier.
5533 pattack->staydown[1] = GET_INT_ARG(2); //Riseattack time addition and toggle.
5534 break;
5535 case CMD_MODEL_DOT:
5536 // Cause damage over time effect.
5537 attack.dot_index = GET_INT_ARG(1); //Index.
5538 attack.dot_time = GET_INT_ARG(2); //Time to expiration.
5539 attack.dot = GET_INT_ARG(3); //Mode, see common_dot.
5540 attack.dot_force = GET_INT_ARG(4); //Amount per tick.
5541 attack.dot_rate = GET_INT_ARG(5); //Tick delay.
5542 break;
5543 case CMD_MODEL_FORCEMAP:
5544 // force color map change for specified time
5545 pattack = (!newanim && newchar->smartbomb) ? newchar->smartbomb : &attack;
5546 pattack->forcemap = GET_INT_ARG(1);
5547 pattack->maptime = GET_INT_ARG(2) * GAME_SPEED;
5548 break;
5549 case CMD_MODEL_IDLE:
5550 idle = GET_INT_ARG(1);
5551 break;
5552 case CMD_MODEL_MOVE:
5553 move = GET_INT_ARG(1);
5554 break;
5555 case CMD_MODEL_MOVEZ:
5556 movez = GET_INT_ARG(1);
5557 break;
5558 case CMD_MODEL_MOVEA:
5559 movea = GET_INT_ARG(1);
5560 break;
5561 case CMD_MODEL_SETA:
5562 seta = GET_INT_ARG(1);
5563 break;
5564 case CMD_MODEL_FSHADOW:
5565 frameshadow = GET_INT_ARG(1);
5566 break;
5567 case CMD_MODEL_RANGE:
5568 if(!newanim) {
5569 shutdownmessage = "Cannot set range: no animation!";
5570 goto lCleanup;
5572 newanim->range[0] = GET_INT_ARG(1);
5573 newanim->range[1] = GET_INT_ARG(2);
5574 break;
5575 case CMD_MODEL_RANGEZ:
5576 if(!newanim) {
5577 shutdownmessage = "Cannot set rangez: no animation!";
5578 goto lCleanup;
5580 newanim->range[2] = GET_INT_ARG(1);
5581 newanim->range[3] = GET_INT_ARG(2);
5582 break;
5583 case CMD_MODEL_RANGEA:
5584 if(!newanim) {
5585 shutdownmessage = "Cannot set rangea: no animation!";
5586 goto lCleanup;
5588 newanim->range[4] = GET_INT_ARG(1);
5589 newanim->range[5] = GET_INT_ARG(2);
5590 break;
5591 case CMD_MODEL_RANGEB:
5592 if(!newanim) {
5593 shutdownmessage = "Cannot set rangeb: no animation!";
5594 goto lCleanup;
5596 newanim->range[6] = GET_INT_ARG(1);
5597 newanim->range[7] = GET_INT_ARG(2);
5598 break;
5599 case CMD_MODEL_FRAME:
5601 if(!newanim) {
5602 shutdownmessage = "Cannot add frame: animation not specified!";
5603 goto lCleanup;
5605 peek = 0;
5606 if(frameset && framecount >= 0)
5607 framecount = -framecount;
5608 while(!frameset) {
5609 value3 = findarg(buf + pos + peek, 0);
5610 if(stricmp(value3, "frame") == 0)
5611 framecount++;
5612 if((stricmp(value3, "anim") == 0) || (pos + peek >= size))
5613 frameset = 1;
5614 // Go to next line
5615 while(buf[pos + peek] && buf[pos + peek] != '\n'
5616 && buf[pos + peek] != '\r')
5617 ++peek;
5618 while(buf[pos + peek] == '\n' || buf[pos + peek] == '\r')
5619 ++peek;
5621 value = GET_ARG(1);
5622 //printf("frame count: %d\n",framecount);
5623 //printf("Load sprite '%s'...\n", value);
5624 index = loadsprite(value, offset[0], offset[1], PIXEL_8); //don't use palette for the sprite since it will one palette from the entity's remap list in 24bit mode
5625 if(pixelformat == PIXEL_x8) {
5626 // for old mod just give it a default palette
5627 if(newchar->palette == NULL) {
5628 newchar->palette = malloc(PAL_BYTES);
5629 if(loadimagepalette(value, packfile, newchar->palette)
5630 == 0) {
5631 shutdownmessage = "Failed to load palette!";
5632 goto lCleanup;
5635 if(index >= 0) {
5636 sprite_map[index].sprite->palette = newchar->palette;
5637 sprite_map[index].sprite->pixelformat = pixelformat;
5640 if((index >= 0) && (maskindex >= 0)) {
5641 sprite_map[index].sprite->mask = sprite_map[maskindex].sprite;
5642 maskindex = -1;
5644 // Adjust coords: add offsets and change size to coords
5645 bbox_con[0] = bbox[0] - offset[0];
5646 bbox_con[1] = bbox[1] - offset[1];
5647 bbox_con[2] = bbox[2] + bbox_con[0];
5648 bbox_con[3] = bbox[3] + bbox_con[1];
5649 bbox_con[4] = bbox[4];
5650 attack.attack_coords[0] = abox[0] - offset[0];
5651 attack.attack_coords[1] = abox[1] - offset[1];
5652 attack.attack_coords[2] = abox[2] + attack.attack_coords[0];
5653 attack.attack_coords[3] = abox[3] + attack.attack_coords[1];
5654 //attack.attack_coords[4] = abox[4];
5655 if(platform[0] == 99999) // old style
5657 platform_con[0] = 0;
5658 platform_con[1] = 3;
5659 platform_con[2] = platform[2] - offset[0];
5660 platform_con[3] = platform[3] - offset[0];
5661 platform_con[4] = platform[4] - offset[0];
5662 platform_con[5] = platform[5] - offset[0];
5663 platform_con[6] = platform[6] + 3;
5664 } else // wall style
5666 platform_con[0] = platform[0] - offset[0];
5667 platform_con[1] = platform[1] - offset[1];
5668 platform_con[2] = platform[2];
5669 platform_con[3] = platform[3];
5670 platform_con[4] = platform[4];
5671 platform_con[5] = platform[5];
5672 platform_con[6] = platform[6];
5674 platform_con[6] = platform[6];
5675 platform_con[7] = platform[7];
5676 if(shadow_set) {
5677 shadow_coords[0] = shadow_xz[0] - offset[0];
5678 shadow_coords[1] = shadow_xz[1] - offset[1];
5679 } else {
5680 shadow_coords[0] = shadow_coords[1] = 0;
5683 curframe =
5684 addframe(newanim, index, framecount, delay, (unsigned char) idle,
5685 bbox_con, &attack, move, movez, movea, seta, platform_con,
5686 frameshadow, shadow_coords, soundtoplay, &drawmethod);
5688 memset(bbox_con, 0, sizeof(bbox_con));
5689 soundtoplay = -1;
5691 break;
5692 case CMD_MODEL_ALPHAMASK:
5693 if(!newanim) {
5694 shutdownmessage = "Cannot add alpha mask: animation not specified!";
5695 goto lCleanup;
5697 if(maskindex >= 0) {
5698 shutdownmessage =
5699 "Cannot add alpha mask: a mask has already been specified for this frame!";
5700 goto lCleanup;
5702 value = GET_ARG(1);
5703 //printf("frame count: %d\n",framecount);
5704 //printf("Load sprite '%s'...\n", value);
5705 index = loadsprite(value, offset[0], offset[1], PIXEL_8); //don't use palette for the mask
5706 maskindex = index;
5707 break;
5708 case CMD_MODEL_FLIPFRAME:
5709 newanim->flipframe = GET_INT_ARG(1);
5710 break;
5711 case CMD_MODEL_FOLLOWANIM:
5712 newanim->followanim = GET_INT_ARG(1);
5713 if(newanim->followanim > dyn_anim_custom_maxvalues.max_follows)
5714 newanim->followanim = dyn_anim_custom_maxvalues.max_follows;
5715 if(newanim->followanim < 0)
5716 newanim->followanim = 0;
5717 break;
5718 case CMD_MODEL_FOLLOWCOND:
5719 newanim->followcond = GET_INT_ARG(1);
5720 break;
5721 case CMD_MODEL_COUNTERFRAME:
5722 newanim->counterframe[0] = GET_INT_ARG(1);
5723 newanim->counterframe[1] = GET_INT_ARG(1);
5724 newanim->counterframe[2] = GET_INT_ARG(2);
5725 newanim->counterframe[3] = GET_INT_ARG(3);
5726 break;
5727 case CMD_MODEL_COUNTERRANGE:
5728 newanim->counterframe[0] = GET_INT_ARG(1);
5729 newanim->counterframe[1] = GET_INT_ARG(2);
5730 newanim->counterframe[2] = GET_INT_ARG(3);
5731 newanim->counterframe[3] = GET_INT_ARG(4);
5732 break;
5733 case CMD_MODEL_WEAPONFRAME:
5734 newanim->weaponframe = malloc(2 * sizeof(newanim->weaponframe));
5735 memset(newanim->weaponframe, 0, 2 * sizeof(newanim->weaponframe));
5736 newanim->weaponframe[0] = GET_INT_ARG(1);
5737 newanim->weaponframe[1] = GET_INT_ARG(2);
5738 break;
5739 case CMD_MODEL_QUAKEFRAME:
5740 newanim->quakeframe[0] = GET_INT_ARG(1);
5741 newanim->quakeframe[1] = GET_INT_ARG(2);
5742 newanim->quakeframe[2] = GET_INT_ARG(3);
5743 newanim->quakeframe[3] = 0;
5744 break;
5745 case CMD_MODEL_SUBENTITY:
5746 case CMD_MODEL_CUSTENTITY:
5747 value = GET_ARG(1);
5748 if(value[0])
5749 newanim->subentity = get_cached_model_index(value);
5750 break;
5751 case CMD_MODEL_SPAWNFRAME:
5752 newanim->spawnframe = malloc(5 * sizeof(newanim->spawnframe));
5753 memset(newanim->spawnframe, 0, 5 * sizeof(newanim->spawnframe));
5754 newanim->spawnframe[0] = GET_FLOAT_ARG(1);
5755 newanim->spawnframe[1] = GET_FLOAT_ARG(2);
5756 newanim->spawnframe[2] = GET_FLOAT_ARG(3);
5757 newanim->spawnframe[3] = GET_FLOAT_ARG(4);
5758 newanim->spawnframe[4] = GET_FLOAT_ARG(5);
5759 break;
5760 case CMD_MODEL_SUMMONFRAME:
5761 newanim->summonframe = malloc(5 * sizeof(newanim->summonframe));
5762 memset(newanim->summonframe, 0, 5 * sizeof(newanim->summonframe));
5763 newanim->summonframe[0] = GET_FLOAT_ARG(1);
5764 newanim->summonframe[1] = GET_FLOAT_ARG(2);
5765 newanim->summonframe[2] = GET_FLOAT_ARG(3);
5766 newanim->summonframe[3] = GET_FLOAT_ARG(4);
5767 newanim->summonframe[4] = GET_FLOAT_ARG(5);
5768 break;
5769 case CMD_MODEL_UNSUMMONFRAME:
5770 newanim->unsummonframe = GET_INT_ARG(1);
5771 break;
5772 case CMD_MODEL_AT_SCRIPT:
5773 if(ani_id < 0) {
5774 shutdownmessage = "command '@script' must follow an animation!";
5775 goto lCleanup;
5777 if(!scriptbuf[0]) { // if empty, paste the main function text here
5778 strcat(scriptbuf, pre_text);
5780 scriptbuf[strlen(scriptbuf) - strlen(sur_text)] = 0; // cut last chars
5781 if(script_id != ani_id) { // if expression 1
5782 sprintf(namebuf, ifid_text, ani_id);
5783 strcat(scriptbuf, namebuf);
5784 script_id = ani_id;
5786 scriptbuf[strlen(scriptbuf) - strlen(endifid_text)] = 0; // cut last chars
5787 while(strncmp(buf + pos, "@script", 7)) {
5788 pos++;
5790 pos += 7;
5791 while(strncmp(buf + pos, "@end_script", 11)) {
5792 len = strlen(scriptbuf);
5793 scriptbuf[len] = *(buf + pos);
5794 scriptbuf[len + 1] = 0;
5795 pos++;
5797 pos += 11;
5798 strcat(scriptbuf, endifid_text); // put back last chars
5799 strcat(scriptbuf, sur_text); // put back last chars
5800 break;
5801 case CMD_MODEL_AT_CMD:
5802 //translate @cmd into script function call
5803 if(ani_id < 0) {
5804 shutdownmessage = "command '@cmd' must follow an animation!";
5805 goto lCleanup;
5807 if(!scriptbuf[0]) { // if empty, paste the main function text here
5808 strcat(scriptbuf, pre_text);
5810 scriptbuf[strlen(scriptbuf) - strlen(sur_text)] = 0; // cut last chars
5811 if(script_id != ani_id) { // if expression 1
5812 sprintf(namebuf, ifid_text, ani_id);
5813 strcat(scriptbuf, namebuf);
5814 script_id = ani_id;
5816 j = 1;
5817 value = GET_ARG(j);
5818 scriptbuf[strlen(scriptbuf) - strlen(endifid_text)] = 0; // cut last chars
5819 if(value && value[0]) {
5820 sprintf(namebuf, if_text, curframe); //only execute in current frame
5821 strcat(scriptbuf, namebuf);
5822 sprintf(namebuf, call_text, value);
5823 strcat(scriptbuf, namebuf);
5824 do { //argument and comma
5825 j++;
5826 value = GET_ARG(j);
5827 if(value && value[0]) {
5828 if(j != 2)
5829 strcat(scriptbuf, comma_text);
5830 strcat(scriptbuf, value);
5832 } while(value && value[0]);
5834 strcat(scriptbuf, endcall_text);
5835 strcat(scriptbuf, endif_text); //end of if
5836 strcat(scriptbuf, endifid_text); // put back last chars
5837 strcat(scriptbuf, sur_text); // put back last chars
5838 break;
5839 default:
5840 if(command && command[0])
5841 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
5845 next_line:
5846 pos += getNewLineStart(buf + pos);
5847 line++;
5851 tempInt = 1;
5852 if(scriptbuf[0]) {
5853 //printf("\n%s\n", scriptbuf);
5854 if(!Script_IsInitialized(newchar->scripts.animation_script))
5855 Script_Init(newchar->scripts.animation_script, newchar->name, 0);
5856 tempInt = Script_AppendText(newchar->scripts.animation_script, scriptbuf, filename);
5857 //Interpreter_OutputPCode(newchar->scripts.animation_script.pinterpreter, "code");
5858 writeToScriptLog("\n####animationscript function main#####\n# ");
5859 writeToScriptLog(filename);
5860 writeToScriptLog("\n########################################\n");
5861 writeToScriptLog(scriptbuf);
5863 if(!newchar->isSubclassed)
5864 Script_Compile(newchar->scripts.animation_script);
5866 if(!tempInt) // parse script failed
5868 shutdownmessage = "Error parsing function main of animation script in file '%s'!";
5869 goto lCleanup;
5871 // We need a little more work to initialize the new A.I. types if they are not loaded from file
5872 if(newchar->aiattack == -1)
5873 newchar->aiattack = 0;
5874 if(newchar->aimove == -1)
5875 newchar->aimove = 0;
5876 //if(!newchar->offscreenkill) newchar->offscreenkill = 1000;
5878 if(newchar->risetime[0] == -1) {
5879 if(newchar->type == TYPE_PLAYER) {
5880 if(newchar->animation[ANI_RISEATTACK])
5881 newchar->risetime[0] = GAME_SPEED / 2;
5882 else
5883 newchar->risetime[0] = GAME_SPEED;
5884 } else if(newchar->type == TYPE_ENEMY || newchar->type == TYPE_NPC) {
5885 newchar->risetime[0] = 0;
5889 if(newchar->hostile < 0) { // not been initialized, so initialize it
5890 switch (newchar->type) {
5891 case TYPE_ENEMY:
5892 newchar->hostile = TYPE_PLAYER;
5893 break;
5894 case TYPE_PLAYER: // dont really needed, since you don't need A.I. control for players
5895 newchar->hostile = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
5896 break;
5897 case TYPE_TRAP:
5898 newchar->hostile = TYPE_ENEMY | TYPE_PLAYER;
5899 case TYPE_OBSTACLE:
5900 newchar->hostile = 0;
5901 break;
5902 case TYPE_SHOT: // only target enemies
5903 newchar->hostile = TYPE_ENEMY;
5904 break;
5905 case TYPE_NPC: // default npc behivior
5906 newchar->hostile = TYPE_ENEMY;
5907 break;
5911 if(newchar->candamage < 0) { // not been initialized, so initialize it
5912 switch (newchar->type) {
5913 case TYPE_ENEMY:
5914 newchar->candamage = TYPE_PLAYER | TYPE_SHOT;
5915 if(newchar->subtype == SUBTYPE_ARROW)
5916 newchar->candamage |= TYPE_OBSTACLE;
5917 break;
5918 case TYPE_PLAYER:
5919 newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
5920 break;
5921 case TYPE_TRAP:
5922 newchar->candamage = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5923 case TYPE_OBSTACLE:
5924 newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
5925 break;
5926 case TYPE_SHOT:
5927 newchar->candamage = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5928 break;
5929 case TYPE_NPC:
5930 newchar->candamage = TYPE_ENEMY | TYPE_OBSTACLE;
5931 break;
5932 case TYPE_ITEM:
5933 newchar->candamage = TYPE_PLAYER;
5934 break;
5938 if(newchar->projectilehit < 0) { // not been initialized, so initialize it
5939 switch (newchar->type) {
5940 case TYPE_ENEMY:
5941 newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5942 break;
5943 case TYPE_PLAYER:
5944 newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5945 break;
5946 case TYPE_TRAP: // hmm, don't really needed
5947 newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5948 case TYPE_OBSTACLE: // hmm, don't really needed
5949 newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5950 break;
5951 case TYPE_SHOT: // hmm, don't really needed
5952 newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5953 break;
5954 case TYPE_NPC:
5955 newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
5956 break;
5960 if(newchar->jumpspeed < 0)
5961 newchar->jumpspeed = MAX(newchar->speed, 1);
5963 if(blendfx_is_set == 0) {
5964 if(newchar->alpha) {
5965 blendfx[newchar->alpha - 1] = 1;
5967 if(newchar->gfxshadow || newchar->shadow) {
5968 blendfx[BLEND_MULTIPLY] = 1;
5971 // we need to convert 8bit colourmap into 24bit palette
5972 if(pixelformat == PIXEL_x8) {
5973 convert_map_to_palette(newchar, mapflag);
5976 printf("Loading '%s' from %s\n", newchar->name, filename);
5978 lCleanup:
5979 freeAndNull((void**) &buf);
5980 freeAndNull((void**) &scriptbuf);
5982 if(!shutdownmessage)
5983 return newchar;
5985 shutdown(1, "Fatal Error in load_cached_model, file: %s, line %d, message: %s\n", filename, line,
5986 shutdownmessage);
5987 return NULL;
5992 int is_set(s_model * model, int m) { // New function to determine if a freespecial has been set
5993 int i;
5995 for(i = 0; i < model->specials_loaded; i++) {
5996 if(model->special[i][MAX_SPECIAL_INPUTS - 2] == m) {
5997 return 1;
6001 return 0;
6004 int load_script_setting() {
6005 char *filename = "data/script.txt";
6006 char *buf, *command;
6007 ptrdiff_t pos = 0;
6008 size_t size = 0;
6009 ArgList arglist;
6010 char argbuf[MAX_ARG_LEN + 1] = "";
6012 if(buffer_pakfile(filename, &buf, &size) != 1)
6013 return 0;
6015 while(pos < size) {
6016 ParseArgs(&arglist, buf + pos, argbuf);
6017 command = GET_ARG(0);
6018 if(command && command[0]) {
6019 if(stricmp(command, "maxscriptvars") == 0) // each script can have a variable list that can be accessed by index
6021 max_script_vars = GET_INT_ARG(1);
6022 if(max_script_vars < 0)
6023 max_script_vars = 0;
6024 } else if(stricmp(command, "maxentityvars") == 0) // each entity can have a variable list that can be accessed by index
6026 max_entity_vars = GET_INT_ARG(1);
6027 if(max_entity_vars < 0)
6028 max_entity_vars = 0;
6029 } else if(stricmp(command, "maxindexedvars") == 0) // a global variable list that can be accessed by index
6031 max_indexed_vars = GET_INT_ARG(1);
6032 if(max_indexed_vars < 0)
6033 max_indexed_vars = 0;
6034 } else if(stricmp(command, "maxglobalvars") == 0) // for global_var_list, default to 2048
6036 max_global_vars = GET_INT_ARG(1);
6037 if(max_global_vars < 0)
6038 max_global_vars = 0;
6039 } else if(stricmp(command, "keyscriptrate") == 0) // Rate that keyscripts fire when holding a key.
6041 keyscriptrate = GET_INT_ARG(1);
6044 // Go to next line
6045 pos += getNewLineStart(buf + pos);
6048 freeAndNull((void**) &buf);
6049 return 1;
6052 // Load / cache all models
6053 int load_models() {
6054 char filename[128] = "data/models.txt";
6055 int i;
6056 char *buf;
6057 size_t size;
6058 ptrdiff_t pos;
6059 char *command;
6060 int line = 0;
6062 char tmpBuff[128] = { "" };
6063 int maxanim = MAX_ANIS; // temporary counter
6065 ArgList arglist;
6066 char argbuf[MAX_ARG_LEN + 1] = "";
6067 modelstxtCommands cmd;
6068 int modelLoadCount = 0;
6070 free_modelcache();
6072 if(isLoadingScreenTypeBg(loadingbg[0].set)) {
6073 // New alternative background path for PSP
6074 if(custBkgrds != NULL) {
6075 strcpy(tmpBuff, custBkgrds);
6076 strncat(tmpBuff, "loading", 7);
6077 load_background(tmpBuff, 0);
6078 } else
6079 load_background("data/bgs/loading", 0);
6080 standard_palette(1);
6082 if(isLoadingScreenTypeBar(loadingbg[0].set)) {
6083 lifebar_colors();
6084 init_colourtable();
6087 update_loading(&loadingbg[0], -1, 1); // initialize the update screen
6089 // reload default values
6090 dyn_anim_custom_maxvalues = dyn_anim_default_custom_maxvalues;
6092 // free old values
6093 freeAnims();
6095 if(custModels != NULL) {
6096 strcpy(filename, "data/");
6097 strcat(filename, custModels);
6099 // Read file
6100 if(buffer_pakfile(filename, &buf, &size) != 1)
6101 shutdown(1, "Error loading model list from %s", filename);
6103 pos = 0;
6104 while(pos < size) // peek global settings
6106 line++;
6107 ParseArgs(&arglist, buf + pos, argbuf);
6108 command = GET_ARG(0);
6109 cmd = getModelstxtCommand(modelstxtcmdlist, command);
6110 switch (cmd) {
6111 case CMD_MODELSTXT_MAXIDLES:
6112 // max idle stances
6113 dyn_anim_custom_maxvalues.max_idles = MAX(GET_INT_ARG(1), MAX_IDLES);
6114 break;
6115 case CMD_MODELSTXT_MAXWALKS:
6116 dyn_anim_custom_maxvalues.max_walks = MAX(GET_INT_ARG(1), MAX_WALKS);
6117 break;
6118 case CMD_MODELSTXT_MAXBACKWALKS:
6119 // max backward walks
6120 dyn_anim_custom_maxvalues.max_backwalks = MAX(GET_INT_ARG(1), MAX_BACKWALKS);
6121 break;
6122 case CMD_MODELSTXT_MAXUPS:
6123 // max up walks
6124 dyn_anim_custom_maxvalues.max_ups = MAX(GET_INT_ARG(1), MAX_UPS);
6125 break;
6126 case CMD_MODELSTXT_MAXDOWNS:
6127 // max down walks
6128 dyn_anim_custom_maxvalues.max_downs = MAX(GET_INT_ARG(1), MAX_DOWNS);
6129 break;
6130 case CMD_MODELSTXT_MAXATTACKTYPES:
6131 // max attacktype/pain/fall/die
6132 dyn_anim_custom_maxvalues.max_attack_types = MAX(GET_INT_ARG(1) + STA_ATKS, MAX_ATKS);
6133 break;
6134 case CMD_MODELSTXT_MAXFOLLOWS:
6135 // max follow-ups
6136 dyn_anim_custom_maxvalues.max_follows = MAX(GET_INT_ARG(1), MAX_FOLLOWS);
6137 break;
6138 case CMD_MODELSTXT_MAXFREESPECIALS:
6139 // max freespecials
6140 dyn_anim_custom_maxvalues.max_freespecials = MAX(GET_INT_ARG(1), MAX_SPECIALS);
6141 break;
6142 case CMD_MODELSTXT_MAXATTACKS:
6143 dyn_anim_custom_maxvalues.max_attacks = MAX(GET_INT_ARG(1), MAX_ATTACKS);
6144 break;
6145 case CMD_MODELSTXT_MUSIC:
6146 music(GET_ARG(1), 1, atol(GET_ARG(2)));
6147 break;
6148 case CMD_MODELSTXT_LOAD:
6149 // Add path to cache list
6150 modelLoadCount++;
6151 cache_model(GET_ARG(1), GET_ARG(2), 1);
6152 break;
6153 case CMD_MODELSTXT_COLOURSELECT:
6154 // 6-2-2005 if string for colourselect found
6155 colourselect = GET_INT_ARG(1); // 6-2-2005
6156 break;
6157 case CMD_MODELSTXT_SPDIRECTION:
6158 // Select Player Direction for select player screen
6159 spdirection[0] = GET_INT_ARG(1);
6160 spdirection[1] = GET_INT_ARG(2);
6161 spdirection[2] = GET_INT_ARG(3);
6162 spdirection[3] = GET_INT_ARG(4);
6163 break;
6164 case CMD_MODELSTXT_AUTOLAND:
6165 // New flag to determine if a player auto lands when thrown by another player (2 completely disables the ability to land)
6166 autoland = GET_INT_ARG(1);
6167 break;
6168 case CMD_MODELSTXT_NOLOST:
6169 // this is use for dont lost your weapon if you grab a enemy flag it to 1 to no drop by tails
6170 nolost = GET_INT_ARG(1);
6171 break;
6172 case CMD_MODELSTXT_AJSPECIAL:
6173 // Flag to determine if a + j executes special
6174 ajspecial = GET_INT_ARG(1);
6175 break;
6176 case CMD_MODELSTXT_NOCOST:
6177 // Nocost set in models.txt
6178 nocost = GET_INT_ARG(1);
6179 break;
6180 case CMD_MODELSTXT_NOCHEATS:
6181 //disable cheat option in menu
6182 forcecheatsoff = GET_INT_ARG(1);
6183 break;
6184 case CMD_MODELSTXT_NODROPEN:
6185 nodropen = 1;
6186 break;
6187 case CMD_MODELSTXT_KNOW:
6188 // Just add path to cache list
6189 cache_model(GET_ARG(1), GET_ARG(2), 0);
6190 break;
6191 case CMD_MODELSTXT_NOAIRCANCEL:
6192 noaircancel = GET_INT_ARG(1);
6193 break;
6194 case CMD_MODELSTXT_NOMAXRUSHRESET:
6195 nomaxrushreset[4] = GET_INT_ARG(1);
6196 break;
6197 case CMD_MODELSTXT_MPBLOCK:
6198 // Take from MP first?
6199 mpblock = GET_INT_ARG(1);
6200 break;
6201 case CMD_MODELSTXT_BLOCKRATIO:
6202 // Nullify or reduce damage?
6203 blockratio = GET_INT_ARG(1);
6204 break;
6205 case CMD_MODELSTXT_NOCHIPDEATH:
6206 nochipdeath = GET_INT_ARG(1);
6207 break;
6208 case CMD_MODELSTXT_LIFESCORE:
6209 lifescore = GET_INT_ARG(1);
6210 break;
6211 case CMD_MODELSTXT_CREDSCORE:
6212 // Number of points needed to earn a 1-up
6213 credscore = GET_INT_ARG(1);
6214 break;
6215 case CMD_MODELSTXT_VERSUSDAMAGE:
6216 // Number of points needed to earn a credit
6217 versusdamage = GET_INT_ARG(1);
6218 if(versusdamage == 0 || versusdamage == 1)
6219 savedata.mode = versusdamage ^ 1;
6220 break;
6221 case CMD_MODELSTXT_COMBODELAY:
6222 combodelay = GET_INT_ARG(1);
6223 break;
6224 default:
6225 if(command && *command)
6226 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
6229 // Go to next line
6230 pos += getNewLineStart(buf + pos);
6232 // calculate max animations
6233 dyn_anim_custom_maxvalues.max_animations += (dyn_anim_custom_maxvalues.max_attack_types - MAX_ATKS) * 6 + // multply by 5, for fall/die/pain/rise/blockpain/riseattack
6234 (dyn_anim_custom_maxvalues.max_follows - MAX_FOLLOWS) +
6235 (dyn_anim_custom_maxvalues.max_freespecials - MAX_SPECIALS) +
6236 (dyn_anim_custom_maxvalues.max_attacks - MAX_ATTACKS) +
6237 (dyn_anim_custom_maxvalues.max_idles - MAX_IDLES) +
6238 (dyn_anim_custom_maxvalues.max_walks - MAX_WALKS) + (dyn_anim_custom_maxvalues.max_ups - MAX_UPS) + (dyn_anim_custom_maxvalues.max_downs - MAX_DOWNS) + (dyn_anim_custom_maxvalues.max_backwalks - MAX_BACKWALKS);
6240 // alloc indexed animation ids
6241 int** dyn_anim_custom_max_ptr_arr = (int**) &dyn_anim_custom_max_ptr;
6242 int** dyn_anims_arr = (int**) &dyn_anims;
6243 int** dyn_anims_default_arr = (int**) &default_dyn_anims;
6244 char* dyn_anims_default_sizes_arr = (char*) &default_dyn_anims_sizes;
6245 unsigned j;
6247 for(i = 0; i < dyn_anim_itemcount; i++) {
6248 dyn_anims_arr[i] = malloc(sizeof(int) * (*dyn_anim_custom_max_ptr_arr[i]));
6249 memcpy(dyn_anims_arr[i], dyn_anims_default_arr[i], sizeof(int) * dyn_anims_default_sizes_arr[i]);
6250 for(j = dyn_anims_default_sizes_arr[i]; j < *dyn_anim_custom_max_ptr_arr[i]; j++) {
6251 dyn_anims_arr[i][j] = maxanim++;
6255 // Defer load_cached_model, so you can define models after their nested model.
6256 printf("\n");
6258 for(i = 0, pos = 0; i < models_cached; i++) {
6259 //printf("Checking '%s' '%s'\n", model_cache[i].name, model_cache[i].path);
6260 if(model_cache[i].loadflag) {
6261 load_cached_model(model_cache[i].name, "models.txt", 0);
6262 update_loading(&loadingbg[0], ++pos, modelLoadCount);
6265 printf("\nLoading models...............\tDone!\n");
6267 freeAndNull((void**) &buf);
6269 return 1;
6275 void unload_levelorder() {
6276 int i, j;
6277 for(j = 0; j < MAX_DIFFICULTIES; j++) {
6278 for(i = 0; i < MAX_LEVELS; i++) {
6279 if(levelorder[j][i] != NULL) {
6280 freeAndNull((void**) &levelorder[j][i]->branchname);
6281 freeAndNull((void**) &levelorder[j][i]->filename);
6282 freeAndNull((void**) &levelorder[j][i]);
6285 num_levels[j] = 0;
6286 strcpy(set_names[j], "");
6288 num_difficulties = 0;
6290 if(skipselect) {
6291 for(i = 0; i < MAX_DIFFICULTIES; i++) {
6292 for(j = 0; j < MAX_PLAYERS; j++)
6293 freeAndNull((void**) &((*skipselect)[i][j]));
6295 freeAndNull((void**) &skipselect);
6299 void alloc_levelorder(int diff, char* filename) {
6300 levelorder[diff][num_levels[diff]] = (s_level_entry *) calloc(1, sizeof(s_level_entry));
6301 levelorder[diff][num_levels[diff]]->branchname = strdup(branch_name);
6302 levelorder[diff][num_levels[diff]]->filename = strdup(filename);
6305 // Add a level to the level order
6306 void add_level(char *filename, int diff) {
6307 if(diff > MAX_DIFFICULTIES)
6308 return;
6309 if(num_levels[diff] >= MAX_LEVELS)
6310 shutdown(1, "Too many entries in level order (max. %i)!", MAX_LEVELS);
6312 alloc_levelorder(diff, filename);
6314 levelorder[diff][num_levels[diff]]->z_coords[0] = (z_coords[0] > 0) ? z_coords[0] : PLAYER_MIN_Z;
6315 levelorder[diff][num_levels[diff]]->z_coords[1] = (z_coords[1] > 0) ? z_coords[1] : PLAYER_MAX_Z;
6316 levelorder[diff][num_levels[diff]]->z_coords[2] = (z_coords[2] > 0) ? z_coords[2] : PLAYER_MIN_Z;
6317 num_levels[diff]++;
6320 // Add a scene to the level order
6321 void add_scene(char *filename, int diff) {
6322 if(diff > MAX_DIFFICULTIES)
6323 return;
6324 if(num_levels[diff] >= MAX_LEVELS)
6325 shutdown(1, "Too many entries in level order (max. %i)!", MAX_LEVELS);
6327 alloc_levelorder(diff, filename);
6329 levelorder[diff][num_levels[diff]]->type = cut_scene;
6330 num_levels[diff]++;
6333 // Add a select screen file to the level order
6334 void add_select(char *filename, int diff) {
6335 if(diff > MAX_DIFFICULTIES)
6336 return;
6337 if(num_levels[diff] >= MAX_LEVELS)
6338 shutdown(1, "Too many entries in level order (max. %i)!", MAX_LEVELS);
6340 alloc_levelorder(diff, filename);
6342 levelorder[diff][num_levels[diff]]->type = select_screen;
6343 num_levels[diff]++;
6346 static void _readbarstatus(char *buf, s_barstatus * pstatus) {
6347 char *value;
6348 ArgList arglist;
6349 char argbuf[MAX_ARG_LEN + 1] = "";
6351 ParseArgs(&arglist, buf, argbuf);
6352 if((value = GET_ARG(1))[0])
6353 pstatus->sizex = atoi(value);
6354 else
6355 return;
6356 if((value = GET_ARG(2))[0])
6357 pstatus->sizey = atoi(value);
6358 else
6359 return;
6360 if((value = GET_ARG(3))[0])
6361 pstatus->noborder = atoi(value);
6362 else
6363 return;
6364 if((value = GET_ARG(4))[0])
6365 pstatus->type = atoi(value);
6366 else
6367 return;
6368 if((value = GET_ARG(5))[0])
6369 pstatus->orientation = atoi(value);
6370 else
6371 return;
6372 if((value = GET_ARG(6))[0])
6373 pstatus->borderlayer = atoi(value);
6374 else
6375 return;
6376 if((value = GET_ARG(7))[0])
6377 pstatus->shadowlayer = atoi(value);
6378 else
6379 return;
6380 if((value = GET_ARG(8))[0])
6381 pstatus->barlayer = atoi(value);
6382 else
6383 return;
6384 if((value = GET_ARG(9))[0])
6385 pstatus->backlayer = atoi(value);
6386 else
6387 return;
6390 // Load list of levels
6391 void load_levelorder() {
6392 static const char *defaulterr = "Error in level order: a set must be specified.";
6393 #define CHKDEF if(current_set<0) { errormessage = (char*) defaulterr; goto lCleanup; }
6394 char filename[128] = "";
6395 int i = 0, j = 0;
6396 char *buf;
6397 size_t size;
6398 int pos;
6399 int current_set;
6400 char *command;
6401 char *arg;
6402 char *errormessage = NULL;
6403 char value[128] = { "" };
6404 int plifeUsed[2] = { 0, 0 };
6405 int elifeUsed[2] = { 0, 0 };
6406 int piconUsed[2] = { 0, 0 };
6407 int piconwUsed[2] = { 0, 0 };
6408 int eiconUsed[4] = { 0, 0, 0, 0 };
6409 int pmpUsed[4] = { 0, 0, 0, 0 };
6410 int plifeXused[4] = { 0, 0, 0, 0 }; // 4-7-2006 New custimizable variable for players 'x'
6411 int plifeNused[4] = { 0, 0, 0, 0 }; // 4-7-2006 New custimizable variable for players 'lives'
6412 int enameused[4] = { 0, 0, 0, 0 }; // 4-7-2006 New custimizable variable for enemy names
6413 int pnameJused[4] = { 0, 0, 0, 0 }; // 1-8-2006 New custimizable variable for players name Select Hero
6414 int pscoreUsed[4] = { 0, 0, 0, 0 }; // 1-8-2006 New custimizable variable for players name Select Hero
6416 ArgList arglist;
6417 char argbuf[MAX_ARG_LEN + 1] = "";
6418 levelOrderCommands cmd;
6419 unsigned line = 1;
6421 unload_levelorder();
6423 if(custLevels != NULL) {
6424 strcpy(filename, "data/");
6425 strcat(filename, custLevels);
6426 } else
6427 strcpy(filename, "data/levels.txt");
6429 // Read file
6431 if(buffer_pakfile(filename, &buf, &size) != 1)
6432 shutdown(1, "Error loading level list from %s", filename);
6434 // Now interpret the contents of buf line by line
6435 pos = 0;
6436 current_set = -1;
6438 // Custom lifebar/timebox/icon positioning and size
6439 picon[0][0] = piconw[0][0] = picon[2][0] = piconw[2][0] = eicon[0][0] = eicon[2][0] = 2;
6440 picon[1][0] = piconw[1][0] = picon[3][0] = piconw[3][0] = eicon[1][0] = eicon[3][0] = 2 + P2_STATS_DIST;
6441 picon[0][1] = piconw[0][1] = picon[1][1] = piconw[1][1] = 2;
6442 picon[2][1] = piconw[2][1] = picon[3][1] = piconw[3][1] = 202;
6443 plife[0][0] = pmp[0][0] = plife[2][0] = pmp[2][0] = elife[0][0] = elife[2][0] = 20;
6444 plife[1][0] = pmp[1][0] = plife[3][0] = pmp[3][0] = elife[1][0] = elife[3][0] = 20 + P2_STATS_DIST;
6445 plife[0][1] = plife[1][1] = 10;
6446 plife[2][1] = plife[3][1] = 210;
6447 pmp[0][1] = pmp[1][1] = 18;
6448 pmp[2][1] = pmp[3][1] = 218;
6450 memset(psmenu, 0, sizeof(int) * 4 * 4);
6452 eicon[0][1] = eicon[1][1] = 19;
6453 eicon[2][1] = eicon[3][1] = 220;
6454 elife[0][1] = elife[1][1] = 27;
6455 elife[2][1] = elife[3][1] = 227;
6457 timeloc[0] = 149;
6458 timeloc[1] = 4;
6459 timeloc[2] = 21;
6460 timeloc[3] = 20;
6461 timeloc[4] = 0;
6463 lbarstatus.sizex = mpbarstatus.sizex = 100;
6464 lbarstatus.sizey = 5;
6465 mpbarstatus.sizey = 3;
6466 lbarstatus.noborder = mpbarstatus.noborder = 0;
6468 // Show Complete Default Values
6469 scomplete[0] = 75;
6470 scomplete[1] = 60;
6471 scomplete[2] = 0;
6472 scomplete[3] = 0;
6473 scomplete[4] = 0;
6474 scomplete[5] = 0;
6476 // Show Complete Y Values
6477 cbonus[0] = lbonus[0] = rbonus[0] = tscore[0] = 10;
6478 cbonus[1] = cbonus[3] = cbonus[5] = cbonus[7] = cbonus[9] = 100;
6479 lbonus[1] = lbonus[3] = lbonus[5] = lbonus[7] = lbonus[9] = 120;
6480 rbonus[1] = rbonus[3] = rbonus[5] = rbonus[7] = rbonus[9] = 140;
6481 tscore[1] = tscore[3] = tscore[5] = tscore[7] = tscore[9] = 160;
6483 // Show Complete X Values
6484 cbonus[2] = lbonus[2] = rbonus[2] = tscore[2] = 100;
6485 cbonus[4] = lbonus[4] = rbonus[4] = tscore[4] = 155;
6486 cbonus[6] = lbonus[6] = rbonus[6] = tscore[6] = 210;
6487 cbonus[8] = lbonus[8] = rbonus[8] = tscore[8] = 265;
6490 while(pos < size) {
6491 ParseArgs(&arglist, buf + pos, argbuf);
6492 command = GET_ARG(0);
6493 cmd = getLevelOrderCommand(levelordercmdlist, command);
6494 switch (cmd) {
6495 case CMD_LEVELORDER_BLENDFX:
6496 for(i = 0; i < MAX_BLENDINGS; i++) {
6497 if(GET_INT_ARG(i + 1))
6498 blendfx[i] = 1;
6499 else
6500 blendfx[i] = 0;
6502 blendfx_is_set = 1;
6503 break;
6504 case CMD_LEVELORDER_SET:
6505 if(num_difficulties >= MAX_DIFFICULTIES) {
6506 errormessage = "Too many sets of levels (check MAX_DIFFICULTIES)!";
6507 goto lCleanup;
6509 ++num_difficulties;
6510 ++current_set;
6511 strncpy(set_names[current_set], GET_ARG(1), MAX_NAME_LEN);
6512 ifcomplete[current_set] = 0;
6513 cansave_flag[current_set] = 1; // default to 1, so the level can be saved
6514 branch_name[0] = 0;
6515 break;
6516 case CMD_LEVELORDER_IFCOMPLETE:
6517 CHKDEF;
6518 ifcomplete[current_set] = GET_INT_ARG(1);
6519 break;
6520 case CMD_LEVELORDER_SKIPSELECT:
6521 CHKDEF;
6522 if(!skipselect) {
6523 skipselect = malloc(sizeof(*skipselect));
6524 memset(skipselect, 0, sizeof(*skipselect));
6527 for(i = 0; i < 4; i++) {
6528 if((arg = GET_ARG(i + 1))[0]) {
6529 if(!(*skipselect)[current_set][i])
6530 (*skipselect)[current_set][i] = malloc(MAX_NAME_LEN + 1);
6531 strncpy((*skipselect)[current_set][i], arg, MAX_NAME_LEN);
6534 break;
6535 case CMD_LEVELORDER_FILE:
6536 CHKDEF;
6537 strncpy(value, GET_ARG(1), 127);
6538 add_level(value, current_set);
6539 break;
6540 case CMD_LEVELORDER_SCENE:
6541 CHKDEF;
6542 strncpy(value, GET_ARG(1), 127);
6543 add_scene(value, current_set);
6544 break;
6545 case CMD_LEVELORDER_SELECT:
6546 CHKDEF;
6547 strncpy(value, GET_ARG(1), 127);
6548 add_select(value, current_set);
6549 break;
6550 case CMD_LEVELORDER_NEXT:
6551 CHKDEF;
6552 // Set 'gonext' flag of last loaded level
6553 if(num_levels[current_set] < 1) {
6554 errormessage = "Error in level order (next before file)!";
6555 goto lCleanup;
6557 levelorder[current_set][num_levels[current_set] - 1]->gonext = 1;
6558 break;
6559 case CMD_LEVELORDER_END:
6560 CHKDEF;
6561 // Set endgame flag of last loaded level
6562 if(num_levels[current_set] < 1) {
6563 errormessage = "Error in level order (next before file)!";
6564 goto lCleanup;
6566 levelorder[current_set][num_levels[current_set] - 1]->gonext = 2;
6567 break;
6568 case CMD_LEVELORDER_LIVES:
6569 // 7-1-2005 credits/lives/singleplayer start here
6570 // used to read the new # of lives/credits from the levels.txt
6571 CHKDEF;
6572 difflives[current_set] = GET_INT_ARG(1);
6573 break;
6574 case CMD_LEVELORDER_DISABLEHOF:
6575 CHKDEF;
6576 noshowhof[current_set] = GET_INT_ARG(1);
6577 break;
6578 case CMD_LEVELORDER_CANSAVE:
6579 // 07-12-31
6580 // 0 this set can't be saved
6581 // 1 save level only
6582 // 2 save player info and level, can't choose player in select menu
6583 CHKDEF;
6584 cansave_flag[current_set] = GET_INT_ARG(1);
6585 break;
6586 case CMD_LEVELORDER_Z:
6587 // 2-10-05 adjust the walkable coordinates
6588 CHKDEF;
6589 z_coords[0] = GET_INT_ARG(1);
6590 z_coords[1] = GET_INT_ARG(2);
6591 z_coords[2] = GET_INT_ARG(3);
6592 break;
6593 case CMD_LEVELORDER_BRANCH:
6594 // 2007-2-22 level branch name
6595 CHKDEF;
6596 strncpy(branch_name, GET_ARG(1), MAX_NAME_LEN);
6597 break;
6598 case CMD_LEVELORDER_P1LIFE:
6599 case CMD_LEVELORDER_P2LIFE:
6600 case CMD_LEVELORDER_P3LIFE:
6601 case CMD_LEVELORDER_P4LIFE:
6602 switch (cmd) {
6603 case CMD_LEVELORDER_P1LIFE:
6604 i = 0;
6605 break;
6606 case CMD_LEVELORDER_P2LIFE:
6607 i = 1;
6608 break;
6609 case CMD_LEVELORDER_P3LIFE:
6610 i = 2;
6611 plifeUsed[0] = 1;
6612 break;
6613 case CMD_LEVELORDER_P4LIFE:
6614 i = 3;
6615 plifeUsed[1] = 1;
6616 break;
6617 default:
6618 assert(0);
6620 if((arg = GET_ARG(1))[0])
6621 plife[i][0] = atoi(arg);
6622 if((arg = GET_ARG(2))[0])
6623 plife[i][1] = atoi(arg);
6624 break;
6625 case CMD_LEVELORDER_P1MP:
6626 case CMD_LEVELORDER_P2MP:
6627 case CMD_LEVELORDER_P3MP:
6628 case CMD_LEVELORDER_P4MP:
6629 switch (cmd) {
6630 case CMD_LEVELORDER_P1MP:
6631 i = 0;
6632 break;
6633 case CMD_LEVELORDER_P2MP:
6634 i = 1;
6635 break;
6636 case CMD_LEVELORDER_P3MP:
6637 i = 2;
6638 break;
6639 case CMD_LEVELORDER_P4MP:
6640 i = 3;
6641 break;
6642 default:
6643 assert(0);
6645 if((arg = GET_ARG(1))[0])
6646 pmp[i][0] = atoi(arg);
6647 if((arg = GET_ARG(2))[0])
6648 pmp[i][1] = atoi(arg);
6649 pmpUsed[i] = 1;
6650 break;
6651 case CMD_LEVELORDER_P1LIFEX:
6652 case CMD_LEVELORDER_P2LIFEX:
6653 case CMD_LEVELORDER_P3LIFEX:
6654 case CMD_LEVELORDER_P4LIFEX:
6655 switch (cmd) {
6656 case CMD_LEVELORDER_P1LIFEX:
6657 j = 0;
6658 break;
6659 case CMD_LEVELORDER_P2LIFEX:
6660 j = 1;
6661 break;
6662 case CMD_LEVELORDER_P3LIFEX:
6663 j = 2;
6664 break;
6665 case CMD_LEVELORDER_P4LIFEX:
6666 j = 3;
6667 break;
6668 default:
6669 assert(0);
6671 for(i = 0; i < 3; i++)
6672 if((arg = GET_ARG(i + 1))[0])
6673 plifeX[j][i] = atoi(arg);
6674 plifeXused[j] = 1;
6675 break;
6676 case CMD_LEVELORDER_P1LIFEN:
6677 case CMD_LEVELORDER_P2LIFEN:
6678 case CMD_LEVELORDER_P3LIFEN:
6679 case CMD_LEVELORDER_P4LIFEN:
6680 switch (cmd) {
6681 case CMD_LEVELORDER_P1LIFEN:
6682 j = 0;
6683 break;
6684 case CMD_LEVELORDER_P2LIFEN:
6685 j = 1;
6686 break;
6687 case CMD_LEVELORDER_P3LIFEN:
6688 j = 2;
6689 break;
6690 case CMD_LEVELORDER_P4LIFEN:
6691 j = 3;
6692 break;
6693 default:
6694 assert(0);
6696 for(i = 0; i < 3; i++)
6697 if((arg = GET_ARG(i + 1))[0])
6698 plifeN[j][i] = atoi(arg);
6699 plifeNused[j] = 1;
6700 break;
6701 case CMD_LEVELORDER_E1LIFE:
6702 case CMD_LEVELORDER_E2LIFE:
6703 case CMD_LEVELORDER_E3LIFE:
6704 case CMD_LEVELORDER_E4LIFE:
6705 switch (cmd) {
6706 case CMD_LEVELORDER_E1LIFE:
6707 i = 0;
6708 break;
6709 case CMD_LEVELORDER_E2LIFE:
6710 i = 1;
6711 break;
6712 case CMD_LEVELORDER_E3LIFE:
6713 i = 2;
6714 elifeUsed[0] = 1;
6715 break;
6716 case CMD_LEVELORDER_E4LIFE:
6717 i = 3;
6718 elifeUsed[1] = 1;
6719 break;
6720 default:
6721 assert(0);
6723 if((arg = GET_ARG(1))[0])
6724 elife[i][0] = atoi(arg);
6725 if((arg = GET_ARG(2))[0])
6726 elife[i][1] = atoi(arg);
6727 break;
6728 case CMD_LEVELORDER_P1ICON:
6729 case CMD_LEVELORDER_P2ICON:
6730 case CMD_LEVELORDER_P3ICON:
6731 case CMD_LEVELORDER_P4ICON:
6732 switch (cmd) {
6733 case CMD_LEVELORDER_P1ICON:
6734 i = 0;
6735 break;
6736 case CMD_LEVELORDER_P2ICON:
6737 i = 1;
6738 break;
6739 case CMD_LEVELORDER_P3ICON:
6740 i = 2;
6741 piconUsed[0] = 1;
6742 break;
6743 case CMD_LEVELORDER_P4ICON:
6744 i = 3;
6745 piconUsed[1] = 1;
6746 break;
6747 default:
6748 assert(0);
6750 if((arg = GET_ARG(1))[0])
6751 picon[i][0] = atoi(arg);
6752 if((arg = GET_ARG(2))[0])
6753 picon[i][1] = atoi(arg);
6754 break;
6755 case CMD_LEVELORDER_P1ICONW:
6756 case CMD_LEVELORDER_P2ICONW:
6757 case CMD_LEVELORDER_P3ICONW:
6758 case CMD_LEVELORDER_P4ICONW:
6759 switch (cmd) {
6760 case CMD_LEVELORDER_P1ICONW:
6761 i = 0;
6762 break;
6763 case CMD_LEVELORDER_P2ICONW:
6764 i = 1;
6765 break;
6766 case CMD_LEVELORDER_P3ICONW:
6767 i = 2;
6768 piconwUsed[0] = 1;
6769 break;
6770 case CMD_LEVELORDER_P4ICONW:
6771 i = 3;
6772 piconwUsed[1] = 1;
6773 break;
6774 default:
6775 assert(0);
6777 if((arg = GET_ARG(1))[0])
6778 piconw[i][0] = atoi(arg);
6779 if((arg = GET_ARG(2))[0])
6780 piconw[i][1] = atoi(arg);
6781 break;
6782 case CMD_LEVELORDER_MP1ICON:
6783 case CMD_LEVELORDER_MP2ICON:
6784 case CMD_LEVELORDER_MP3ICON:
6785 case CMD_LEVELORDER_MP4ICON:
6786 switch (cmd) {
6787 case CMD_LEVELORDER_MP1ICON:
6788 i = 0;
6789 break;
6790 case CMD_LEVELORDER_MP2ICON:
6791 i = 1;
6792 break;
6793 case CMD_LEVELORDER_MP3ICON:
6794 i = 2;
6795 break;
6796 case CMD_LEVELORDER_MP4ICON:
6797 i = 3;
6798 break;
6799 default:
6800 assert(0);
6802 if((arg = GET_ARG(1))[0])
6803 mpicon[i][0] = atoi(arg);
6804 if((arg = GET_ARG(2))[0])
6805 mpicon[i][1] = atoi(arg);
6806 break;
6807 case CMD_LEVELORDER_P1NAMEJ:
6808 case CMD_LEVELORDER_P2NAMEJ:
6809 case CMD_LEVELORDER_P3NAMEJ:
6810 case CMD_LEVELORDER_P4NAMEJ:
6811 switch (cmd) {
6812 case CMD_LEVELORDER_P1NAMEJ:
6813 j = 0;
6814 break;
6815 case CMD_LEVELORDER_P2NAMEJ:
6816 j = 1;
6817 break;
6818 case CMD_LEVELORDER_P3NAMEJ:
6819 j = 2;
6820 break;
6821 case CMD_LEVELORDER_P4NAMEJ:
6822 j = 3;
6823 break;
6824 default:
6825 assert(0);
6827 for(i = 0; i < 7; i++)
6828 if((arg = GET_ARG(i + 1))[0])
6829 pnameJ[j][i] = atoi(arg);
6830 pnameJused[j] = 1;
6831 break;
6832 case CMD_LEVELORDER_P1SCORE:
6833 case CMD_LEVELORDER_P2SCORE:
6834 case CMD_LEVELORDER_P3SCORE:
6835 case CMD_LEVELORDER_P4SCORE:
6836 switch (cmd) {
6837 case CMD_LEVELORDER_P1SCORE:
6838 j = 0;
6839 break;
6840 case CMD_LEVELORDER_P2SCORE:
6841 j = 1;
6842 break;
6843 case CMD_LEVELORDER_P3SCORE:
6844 j = 2;
6845 break;
6846 case CMD_LEVELORDER_P4SCORE:
6847 j = 3;
6848 break;
6849 default:
6850 assert(0);
6852 for(i = 0; i < 7; i++)
6853 if((arg = GET_ARG(i + 1))[0])
6854 pscore[j][i] = atoi(arg);
6855 pscoreUsed[j] = 1;
6856 break;
6857 case CMD_LEVELORDER_P1SHOOT:
6858 case CMD_LEVELORDER_P2SHOOT:
6859 case CMD_LEVELORDER_P3SHOOT:
6860 case CMD_LEVELORDER_P4SHOOT:
6861 switch (cmd) {
6862 case CMD_LEVELORDER_P1SHOOT:
6863 j = 0;
6864 break;
6865 case CMD_LEVELORDER_P2SHOOT:
6866 j = 1;
6867 break;
6868 case CMD_LEVELORDER_P3SHOOT:
6869 j = 2;
6870 break;
6871 case CMD_LEVELORDER_P4SHOOT:
6872 j = 3;
6873 break;
6874 default:
6875 assert(0);
6877 for(i = 0; i < 3; i++)
6878 if((arg = GET_ARG(i + 1))[0])
6879 pshoot[j][i] = atoi(arg);
6880 break;
6881 case CMD_LEVELORDER_P1RUSH:
6882 case CMD_LEVELORDER_P2RUSH:
6883 case CMD_LEVELORDER_P3RUSH:
6884 case CMD_LEVELORDER_P4RUSH:
6885 switch (cmd) {
6886 case CMD_LEVELORDER_P1RUSH:
6887 j = 0;
6888 break;
6889 case CMD_LEVELORDER_P2RUSH:
6890 j = 1;
6891 break;
6892 case CMD_LEVELORDER_P3RUSH:
6893 j = 2;
6894 break;
6895 case CMD_LEVELORDER_P4RUSH:
6896 j = 3;
6897 break;
6898 default:
6899 assert(0);
6901 for(i = 0; i < 8; i++)
6902 if((arg = GET_ARG(i + 1))[0])
6903 prush[j][i] = atoi(arg);
6904 break;
6905 case CMD_LEVELORDER_E1ICON:
6906 case CMD_LEVELORDER_E2ICON:
6907 case CMD_LEVELORDER_E3ICON:
6908 case CMD_LEVELORDER_E4ICON:
6909 switch (cmd) {
6910 case CMD_LEVELORDER_E1ICON:
6911 i = 0;
6912 break;
6913 case CMD_LEVELORDER_E2ICON:
6914 i = 1;
6915 break;
6916 case CMD_LEVELORDER_E3ICON:
6917 i = 2;
6918 eiconUsed[0] = 1;
6919 break;
6920 case CMD_LEVELORDER_E4ICON:
6921 i = 3;
6922 eiconUsed[1] = 1;
6923 break;
6924 default:
6925 assert(0);
6927 if((arg = GET_ARG(1))[0])
6928 eicon[i][0] = atoi(arg);
6929 if((arg = GET_ARG(2))[0])
6930 eicon[i][1] = atoi(arg);
6931 break;
6932 case CMD_LEVELORDER_E1NAME:
6933 case CMD_LEVELORDER_E2NAME:
6934 case CMD_LEVELORDER_E3NAME:
6935 case CMD_LEVELORDER_E4NAME:
6936 switch (cmd) {
6937 case CMD_LEVELORDER_E1NAME:
6938 j = 0;
6939 break;
6940 case CMD_LEVELORDER_E2NAME:
6941 j = 1;
6942 break;
6943 case CMD_LEVELORDER_E3NAME:
6944 j = 2;
6945 break;
6946 case CMD_LEVELORDER_E4NAME:
6947 j = 3;
6948 break;
6949 default:
6950 assert(0);
6952 for(i = 0; i < 3; i++)
6953 if((arg = GET_ARG(i + 1))[0])
6954 ename[j][i] = atoi(arg);
6955 enameused[j] = 1;
6956 break;
6957 case CMD_LEVELORDER_P1SMENU:
6958 case CMD_LEVELORDER_P2SMENU:
6959 case CMD_LEVELORDER_P3SMENU:
6960 case CMD_LEVELORDER_P4SMENU:
6961 switch (cmd) {
6962 case CMD_LEVELORDER_P1SMENU:
6963 j = 0;
6964 break;
6965 case CMD_LEVELORDER_P2SMENU:
6966 j = 1;
6967 break;
6968 case CMD_LEVELORDER_P3SMENU:
6969 j = 2;
6970 break;
6971 case CMD_LEVELORDER_P4SMENU:
6972 j = 3;
6973 break;
6974 default:
6975 assert(0);
6977 for(i = 0; i < 4; i++)
6978 if((arg = GET_ARG(i + 1))[0])
6979 psmenu[j][i] = atoi(arg);
6980 break;
6981 case CMD_LEVELORDER_TIMEICON:
6982 strncpy(timeicon_path, GET_ARG(1), 127);
6983 timeicon = loadsprite(timeicon_path, 0, 0, pixelformat);
6984 if((arg = GET_ARG(2))[0])
6985 timeicon_offsets[0] = atoi(arg);
6986 if((arg = GET_ARG(3))[0])
6987 timeicon_offsets[1] = atoi(arg);
6988 break;
6989 case CMD_LEVELORDER_BGICON:
6990 strncpy(bgicon_path, GET_ARG(1), 127);
6991 bgicon = loadsprite(bgicon_path, 0, 0, pixelformat);
6992 if((arg = GET_ARG(2))[0])
6993 bgicon_offsets[0] = atoi(arg);
6994 if((arg = GET_ARG(3))[0])
6995 bgicon_offsets[1] = atoi(arg);
6996 if((arg = GET_ARG(4))[0])
6997 bgicon_offsets[2] = atoi(arg);
6998 else
6999 bgicon_offsets[2] = HUD_Z / 2;
7000 break;
7001 case CMD_LEVELORDER_OLICON:
7002 strncpy(olicon_path, GET_ARG(1), 127);
7003 olicon = loadsprite(olicon_path, 0, 0, pixelformat);
7004 if((arg = GET_ARG(2))[0])
7005 olicon_offsets[0] = atoi(arg);
7006 if((arg = GET_ARG(3))[0])
7007 olicon_offsets[1] = atoi(arg);
7008 if((arg = GET_ARG(4))[0])
7009 olicon_offsets[2] = atoi(arg);
7010 else
7011 olicon_offsets[2] = HUD_Z * 3;
7012 break;
7013 case CMD_LEVELORDER_TIMELOC:
7014 for(i = 0; i < 6; i++)
7015 if((arg = GET_ARG(i + 1))[0])
7016 timeloc[i] = atoi(arg);
7017 break;
7018 case CMD_LEVELORDER_LBARSIZE:
7019 _readbarstatus(buf + pos, &lbarstatus);
7020 break;
7021 case CMD_LEVELORDER_OLBARSIZE:
7022 _readbarstatus(buf + pos, &olbarstatus);
7023 break;
7024 case CMD_LEVELORDER_MPBARSIZE:
7025 _readbarstatus(buf + pos, &mpbarstatus);
7026 break;
7027 case CMD_LEVELORDER_LBARTEXT:
7028 for(i = 0; i < 4; i++)
7029 if((arg = GET_ARG(i + 1))[0])
7030 lbartext[i] = atoi(arg);
7031 break;
7032 case CMD_LEVELORDER_MPBARTEXT:
7033 for(i = 0; i < 4; i++)
7034 if((arg = GET_ARG(i + 1))[0])
7035 mpbartext[i] = atoi(arg);
7036 break;
7037 case CMD_LEVELORDER_SHOWCOMPLETE:
7038 for(i = 0; i < 6; i++)
7039 if((arg = GET_ARG(i + 1))[0])
7040 scomplete[i] = atoi(arg);
7041 break;
7042 case CMD_LEVELORDER_CLEARBONUS:
7043 for(i = 0; i < 10; i++)
7044 if((arg = GET_ARG(i + 1))[0])
7045 cbonus[i] = atoi(arg);
7046 break;
7047 case CMD_LEVELORDER_RUSHBONUS:
7048 for(i = 0; i < 10; i++)
7049 if((arg = GET_ARG(i + 1))[0])
7050 rbonus[i] = atoi(arg);
7051 break;
7052 case CMD_LEVELORDER_LIFEBONUS:
7053 for(i = 0; i < 10; i++)
7054 if((arg = GET_ARG(i + 1))[0])
7055 lbonus[i] = atoi(arg);
7056 break;
7057 case CMD_LEVELORDER_SCBONUSES:
7058 for(i = 0; i < 4; i++)
7059 if((arg = GET_ARG(i + 1))[0])
7060 scbonuses[i] = atoi(arg);
7061 break;
7062 case CMD_LEVELORDER_TOTALSCORE:
7063 for(i = 0; i < 10; i++)
7064 if((arg = GET_ARG(i + 1))[0])
7065 tscore[i] = atoi(arg);
7066 break;
7067 case CMD_LEVELORDER_MUSICOVERLAP:
7068 CHKDEF;
7069 diffoverlap[current_set] = GET_INT_ARG(1);
7070 break;
7071 case CMD_LEVELORDER_SHOWRUSHBONUS:
7072 showrushbonus = 1;
7073 break;
7074 case CMD_LEVELORDER_NOSLOWFX:
7075 noslowfx = 1;
7076 break;
7077 case CMD_LEVELORDER_EQUALAIRPAUSE:
7078 equalairpause = 1;
7079 break;
7080 case CMD_LEVELORDER_HISCOREBG:
7081 hiscorebg = 1;
7082 break;
7083 case CMD_LEVELORDER_COMPLETEBG:
7084 completebg = 1;
7085 break;
7086 case CMD_LEVELORDER_LOADINGBG:
7087 errormessage =
7088 fill_s_loadingbar(&loadingbg[0], GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3),
7089 GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7),
7090 GET_INT_ARG(8));
7091 if(errormessage)
7092 goto lCleanup;
7093 break;
7094 case CMD_LEVELORDER_LOADINGBG2:
7095 errormessage =
7096 fill_s_loadingbar(&loadingbg[1], GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3),
7097 GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7),
7098 GET_INT_ARG(8));
7099 if(errormessage)
7100 goto lCleanup;
7101 break;
7102 case CMD_LEVELORDER_LOADINGMUSIC:
7103 loadingmusic = GET_INT_ARG(1);
7104 break;
7105 case CMD_LEVELORDER_UNLOCKBG:
7106 unlockbg = 1;
7107 break;
7108 case CMD_LEVELORDER_NOSHARE:
7109 noshare = 1;
7110 break;
7111 case CMD_LEVELORDER_CUSTFADE:
7112 //8-2-2005 custom fade
7113 CHKDEF;
7114 custfade[current_set] = GET_INT_ARG(1);
7115 break;
7116 case CMD_LEVELORDER_CONTINUESCORE:
7117 //8-2-2005 custom fade end
7118 //continuescore
7119 CHKDEF;
7120 continuescore[current_set] = GET_INT_ARG(1);
7121 break;
7122 case CMD_LEVELORDER_CREDITS:
7123 CHKDEF;
7124 diffcreds[current_set] = GET_INT_ARG(1);
7125 break;
7126 case CMD_LEVELORDER_TYPEMP:
7127 //typemp for change for mp restored by time (0) to by enemys (1) or no restore (2) by tails
7128 CHKDEF;
7129 typemp[current_set] = GET_INT_ARG(1);
7130 break;
7131 case CMD_LEVELORDER_SINGLE:
7132 if(current_set < 0) {
7133 for(i = 0; i < MAX_DIFFICULTIES; i++)
7134 maxplayers[i] = ctrlmaxplayers[i] = 1;
7135 } else {
7136 maxplayers[current_set] = ctrlmaxplayers[current_set] = 1;
7138 break;
7139 case CMD_LEVELORDER_MAXPLAYERS:
7140 // 7-1-2005 credits/lives/singleplayer end here
7141 if(current_set < 0) {
7142 maxplayers[0] = GET_INT_ARG(1);
7143 for(i = 0; i < MAX_DIFFICULTIES; i++)
7144 maxplayers[i] = ctrlmaxplayers[i] = maxplayers[0];
7145 } else {
7146 maxplayers[current_set] = ctrlmaxplayers[current_set] = GET_INT_ARG(1);
7148 break;
7149 case CMD_LEVELORDER_NOSAME:
7150 CHKDEF;
7151 same[current_set] = GET_INT_ARG(1);
7152 break;
7153 case CMD_LEVELORDER_RUSH:
7154 rush[0] = GET_INT_ARG(1);
7155 rush[1] = GET_INT_ARG(2);
7156 strncpy(rush_names[0], GET_ARG(3), MAX_NAME_LEN);
7157 rush[2] = GET_INT_ARG(4);
7158 rush[3] = GET_INT_ARG(5);
7159 strncpy(rush_names[1], GET_ARG(6), MAX_NAME_LEN);
7160 rush[4] = GET_INT_ARG(7);
7161 rush[5] = GET_INT_ARG(8);
7162 break;
7163 case CMD_LEVELORDER_MAXWALLHEIGHT:
7164 MAX_WALL_HEIGHT = GET_INT_ARG(1);
7165 if(MAX_WALL_HEIGHT < 0)
7166 MAX_WALL_HEIGHT = 1000;
7167 break;
7168 case CMD_LEVELORDER_SCOREFORMAT:
7169 scoreformat = GET_INT_ARG(1);
7170 break;
7171 default:
7172 if(command && command[0])
7173 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
7176 // Go to next line
7177 pos += getNewLineStart(buf + pos);
7178 line++;
7181 #undef CHKDEF
7183 // Variables without defaults will be auto populated.
7184 if(olbarstatus.sizex == 0) {
7185 olbarstatus = lbarstatus;
7188 if(!plifeUsed[0]) {
7189 plife[2][0] = plife[0][0];
7190 plife[2][1] = plife[2][1] + (plife[0][1] - 10);
7192 if(!plifeUsed[1]) {
7193 plife[3][0] = plife[1][0];
7194 plife[3][1] = plife[3][1] + (plife[1][1] - 10);
7197 if(!elifeUsed[0]) {
7198 elife[2][0] = elife[0][0];
7199 elife[2][1] = elife[2][1] + (elife[0][1] - 27);
7201 if(!elifeUsed[1]) {
7202 elife[3][0] = elife[1][0];
7203 elife[3][1] = elife[3][1] + (elife[1][1] - 27);
7206 if(!piconUsed[0]) {
7207 picon[2][0] = picon[0][0];
7208 picon[2][1] = picon[2][1] + (picon[0][1] - 2);
7210 if(!piconUsed[1]) {
7211 picon[3][0] = picon[1][0];
7212 picon[3][1] = picon[3][1] + (picon[1][1] - 2);
7215 if(!piconwUsed[0]) {
7216 piconw[2][0] = piconw[0][0];
7217 piconw[2][1] = piconw[2][1] + (piconw[0][1] - 2);
7219 if(!piconwUsed[1]) {
7220 piconw[3][0] = piconw[1][0];
7221 piconw[3][1] = piconw[3][1] + (piconw[1][1] - 2);
7224 if(!eiconUsed[0]) {
7225 eicon[2][0] = eicon[0][0];
7226 eicon[2][1] = eicon[2][1] + (eicon[0][1] - 19);
7228 if(!eiconUsed[1]) {
7229 eicon[3][0] = eicon[1][0];
7230 eicon[3][1] = eicon[3][1] + (eicon[1][1] - 19);
7233 if(!pmpUsed[0]) {
7234 pmp[0][0] = plife[0][0];
7235 pmp[0][1] = plife[0][1] + 8;
7237 if(!pmpUsed[1]) {
7238 pmp[1][0] = plife[1][0];
7239 pmp[1][1] = plife[1][1] + 8;
7241 if(!pmpUsed[2]) {
7242 pmp[2][0] = pmp[0][0];
7243 pmp[2][1] = pmp[2][1] + (pmp[0][1] - 18);
7245 if(!pmpUsed[3]) {
7246 pmp[3][0] = pmp[1][0];
7247 pmp[3][1] = pmp[1][1] + (pmp[1][1] - 18);
7250 if(!plifeXused[0]) {
7251 plifeX[0][0] = plife[0][0] + lbarstatus.sizex + 4;
7252 plifeX[0][1] = picon[0][1] + 7;
7254 if(!plifeXused[1]) {
7255 plifeX[1][0] = plife[1][0] + lbarstatus.sizex + 4;
7256 plifeX[1][1] = picon[1][1] + 7;
7258 if(!plifeXused[2]) {
7259 plifeX[2][0] = plife[2][0] + lbarstatus.sizex + 4;
7260 plifeX[2][1] = picon[2][1] + 7;
7262 if(!plifeXused[3]) {
7263 plifeX[3][0] = plife[3][0] + lbarstatus.sizex + 4;
7264 plifeX[3][1] = picon[3][1] + 7;
7266 for(i = 0; i < 4; i++)
7267 if(plifeX[i][2] == -1)
7268 plifeX[i][2] = 0;
7270 if(!plifeNused[0]) {
7271 plifeN[0][0] = plife[0][0] + lbarstatus.sizex + 11;
7272 plifeN[0][1] = picon[0][1];
7274 if(!plifeNused[1]) {
7275 plifeN[1][0] = plife[1][0] + lbarstatus.sizex + 11;
7276 plifeN[1][1] = picon[1][1];
7278 if(!plifeNused[2]) {
7279 plifeN[2][0] = plifeN[0][0];
7280 plifeN[2][1] = picon[2][1];
7282 if(!plifeNused[3]) {
7283 plifeN[3][0] = plifeN[1][0];
7284 plifeN[3][1] = picon[3][1];
7286 for(i = 0; i < 4; i++)
7287 if(plifeN[i][2] == -1)
7288 plifeN[i][2] = 3;
7290 if(!pnameJused[0]) {
7291 pnameJ[0][2] = pnameJ[0][4] = pnameJ[0][0] = plife[0][0] + 1;
7292 pnameJ[0][5] = pnameJ[0][1] = picon[0][1];
7293 pnameJ[0][3] = 10 + pnameJ[0][5];
7295 if(!pnameJused[1]) {
7296 pnameJ[1][2] = pnameJ[1][4] = pnameJ[1][0] = plife[1][0] + 1;
7297 pnameJ[1][5] = pnameJ[1][1] = picon[1][1];
7298 pnameJ[1][3] = 10 + pnameJ[1][5];
7300 if(!pnameJused[2]) {
7301 pnameJ[2][2] = pnameJ[2][4] = pnameJ[2][0] = plife[2][0] + 1;
7302 pnameJ[2][5] = pnameJ[2][1] = picon[2][1];
7303 pnameJ[2][3] = 10 + pnameJ[2][5];
7305 if(!pnameJused[3]) {
7306 pnameJ[3][2] = pnameJ[3][4] = pnameJ[3][0] = plife[3][0] + 1;
7307 pnameJ[3][5] = pnameJ[3][1] = picon[3][1];
7308 pnameJ[3][3] = 10 + pnameJ[3][5];
7310 for(i = 0; i < 4; i++)
7311 if(pnameJ[i][6] == -1)
7312 pnameJ[i][6] = 0;
7314 if(!pscoreUsed[0]) {
7315 pscore[0][0] = plife[0][0] + 1;
7316 pscore[0][1] = picon[0][1];
7318 if(!pscoreUsed[1]) {
7319 pscore[1][0] = plife[1][0] + 1;
7320 pscore[1][1] = picon[1][1];
7322 if(!pscoreUsed[2]) {
7323 pscore[2][0] = plife[2][0] + 1;
7324 pscore[2][1] = picon[2][1];
7326 if(!pscoreUsed[3]) {
7327 pscore[3][0] = plife[3][0] + 1;
7328 pscore[3][1] = picon[3][1];
7330 for(i = 0; i < 4; i++)
7331 if(pscore[i][6] == -1)
7332 pscore[i][6] = 0;
7334 if(!enameused[0]) {
7335 ename[0][0] = elife[0][0] + 1;
7336 ename[0][1] = eicon[0][1];
7338 if(!enameused[1]) {
7339 ename[1][0] = elife[1][0] + 1;
7340 ename[1][1] = eicon[1][1];
7342 if(!enameused[2]) {
7343 ename[2][0] = ename[0][0];
7344 ename[2][1] = eicon[2][1];
7346 if(!enameused[3]) {
7347 ename[3][0] = ename[1][0];
7348 ename[3][1] = eicon[3][1];
7350 for(i = 0; i < 4; i++)
7351 if(ename[i][2] == -1)
7352 ename[i][2] = 0;
7354 branch_name[0] = 0; //clear up branch name, so we can use it in game
7356 for(i = 0; i < 4; i++)
7357 if(pshoot[i][2] == -1)
7358 pshoot[i][2] = 2;
7359 if(timeloc[5] == -1)
7360 timeloc[5] = 3;
7362 if(current_set < 0)
7363 errormessage = "No levels were loaded!";
7365 lCleanup:
7366 freeAndNull((void**) &buf);
7368 if(errormessage)
7369 shutdown(1, "load_levelorder ERROR in %s at %d, msg: %s\n", filename, line, errormessage);
7373 void free_level(s_level * lv) {
7374 int i, j;
7375 s_spawn_script_list_node *templistnode;
7376 s_spawn_script_list_node *templistnode2;
7377 s_spawn_script_cache_node *tempnode;
7378 s_spawn_script_cache_node *tempnode2;
7379 if(!lv)
7380 return;
7381 //offload blending tables
7382 for(i = 0; i < LEVEL_MAX_PALETTES; i++)
7383 for(j = 0; j < MAX_BLENDINGS; j++)
7384 freeAndNull((void**) &lv->blendings[i][j]);
7385 //offload bglayers
7386 for(i = 1; i < lv->numbglayers; i++)
7387 freeAndNull((void**) &lv->bglayers[i].handle);
7389 //offload fglayers
7390 for(i = 0; i < lv->numfglayers; i++)
7391 freeAndNull((void**) &lv->fglayers[i].handle);
7393 //offload textobjs
7394 for(i = 0; i < LEVEL_MAX_TEXTOBJS; i++)
7395 freeAndNull((void**) &lv->textobjs[i].text);
7397 for(i = 0; i < LEVEL_MAX_FILESTREAMS; i++)
7398 freeAndNull((void**) &lv->filestreams[i].buf);
7400 //offload scripts
7401 Script_Clear(&(lv->update_script), 2);
7402 Script_Clear(&(lv->updated_script), 2);
7403 Script_Clear(&(lv->key_script), 2);
7404 Script_Clear(&(lv->level_script), 2);
7405 Script_Clear(&(lv->endlevel_script), 2);
7407 for(i = 0; i < LEVEL_MAX_SPAWNS; i++) {
7408 if(lv->spawnpoints[i].spawn_script_list_head) {
7409 templistnode = lv->spawnpoints[i].spawn_script_list_head;
7410 lv->spawnpoints[i].spawn_script_list_head = NULL;
7411 while(templistnode) {
7412 templistnode2 = templistnode->next;
7413 templistnode->next = NULL;
7414 templistnode->spawn_script = NULL;
7415 free(templistnode);
7416 templistnode = templistnode2;
7421 tempnode = lv->spawn_script_cache_head;
7422 lv->spawn_script_cache_head = NULL;
7423 while(tempnode) {
7424 tempnode2 = tempnode->next;
7425 Script_Clear(tempnode->cached_spawn_script, 2);
7426 freeAndNull((void**) &tempnode->cached_spawn_script);
7427 freeAndNull((void**) &tempnode->filename);
7428 tempnode->next = NULL;
7429 free(tempnode);
7430 tempnode = tempnode2;
7432 freeAndNull((void**) &lv);
7436 void unload_level() {
7437 s_model *temp;
7438 unload_background();
7439 unload_texture();
7440 freepanels();
7441 freescreen(&bgbuffer);
7443 if(level) {
7445 level->pos = 0;
7446 level->advancetime = 0;
7447 level->quake = 0;
7448 level->quaketime = 0;
7449 level->waiting = 0;
7451 printf("Level Unloading: '%s'\n", level->name);
7452 freeAndNull((void**) &level->name);
7453 free_level(level);
7454 level = NULL;
7455 temp = getFirstModel();
7456 do {
7457 if(!temp)
7458 break;
7459 if(temp->unload) {
7460 free_model(temp);
7461 temp = getCurrentModel();
7462 } else
7463 temp = getNextModel();
7464 } while(temp);
7465 printf("Done.\n");
7470 advancex = 0;
7471 advancey = 0;
7472 nojoin = 0;
7473 current_spawn = 0;
7474 groupmin = 100;
7475 groupmax = 100;
7476 scrollminz = 0;
7477 scrollmaxz = 0;
7478 blockade = 0;
7479 level_completed = 0;
7480 tospeedup = 0; // Reset so it sets to normal speed for the next level
7481 reached[0] = reached[1] = reached[2] = reached[3] = 0; // TYPE_ENDLEVEL values reset after level completed //4player
7482 showtimeover = 0;
7483 pause = 0;
7484 endgame = 0;
7485 go_time = 0;
7486 neon_time = 0;
7487 borTime = 0;
7488 cameratype = 0;
7489 light[0] = 128;
7490 light[1] = 64;
7491 gfx_y_offset = 0; // Added so select screen graphics display correctly
7494 char *llHandleCommandSpawnscript(ArgList * arglist, s_spawn_entry * next) {
7495 char *result = NULL;
7496 char *value;
7498 s_spawn_script_cache_node *tempnode;
7499 s_spawn_script_cache_node *tempnode2;
7500 s_spawn_script_list_node *templistnode;
7502 value = GET_ARGP(1);
7504 tempnode = level->spawn_script_cache_head;
7505 if(!next->spawn_script_list_head)
7506 next->spawn_script_list_head = NULL;
7507 templistnode = next->spawn_script_list_head;
7508 if(templistnode) {
7509 while(templistnode->next) {
7510 templistnode = templistnode->next;
7512 templistnode->next = malloc(sizeof(s_spawn_script_list_node));
7513 templistnode = templistnode->next;
7514 } else {
7515 next->spawn_script_list_head = malloc(sizeof(s_spawn_script_list_node));
7516 templistnode = next->spawn_script_list_head;
7518 templistnode->spawn_script = NULL;
7519 templistnode->next = NULL;
7520 if(tempnode) {
7521 while(1) {
7522 if(stricmp(value, tempnode->filename) == 0) {
7523 templistnode->spawn_script = tempnode->cached_spawn_script;
7524 break;
7525 } else {
7526 if(tempnode->next)
7527 tempnode = tempnode->next;
7528 else
7529 break;
7533 if(!templistnode->spawn_script) {
7534 templistnode->spawn_script = alloc_script();
7535 if(!Script_IsInitialized(templistnode->spawn_script))
7536 Script_Init(templistnode->spawn_script, GET_ARGP(0), 0);
7537 else {
7538 result = "Multiple spawn entry script!";
7539 goto lCleanup;
7542 if(load_script(templistnode->spawn_script, value)) {
7543 Script_Compile(templistnode->spawn_script);
7544 if(tempnode) {
7545 tempnode2 = malloc(sizeof(s_spawn_script_cache_node));
7546 tempnode2->cached_spawn_script = templistnode->spawn_script;
7547 tempnode2->filename = strdup(value);
7548 tempnode2->next = NULL;
7549 tempnode->next = tempnode2;
7550 } else {
7551 level->spawn_script_cache_head = malloc(sizeof(s_spawn_script_cache_node));
7552 level->spawn_script_cache_head->cached_spawn_script = templistnode->spawn_script;
7553 level->spawn_script_cache_head->filename = strdup(value);
7554 level->spawn_script_cache_head->next = NULL;
7556 } else {
7557 result = "Failed loading spawn entry script!";
7558 goto lCleanup;
7561 lCleanup:
7562 return result;
7565 static const s_hole default_hole_coords = {
7566 .x = 0,
7567 .z = 240,
7568 .upperleft = 12,
7569 .lowerleft = 1,
7570 .upperright = 200,
7571 .lowerright = 287,
7572 .depth = 45,
7575 void load_level(char *filename) {
7576 char *buf;
7577 size_t size;
7578 ptrdiff_t pos, oldpos;
7579 char *command;
7580 char *value;
7581 char string[128] = { "" };
7582 s_spawn_entry next;
7583 s_model *tempmodel, *cached_model;
7585 int i = 0, j = 0, crlf = 0;
7586 int usemap[MAX_BLENDINGS];
7587 char bgPath[128] = { "" };
7588 s_loadingbar bgPosi = { 0, 0, 0, 0, 0, 0, 0 };
7589 char musicPath[128] = { "" };
7590 u32 musicOffset = 0;
7592 ArgList arglist;
7593 char argbuf[MAX_ARG_LEN + 1] = "";
7595 ArgList arglist2;
7596 char argbuf2[MAX_ARG_LEN + 1] = "";
7598 levelCommands cmd;
7599 levelCommands cmd2;
7600 unsigned line = 1;
7601 char *errormessage = NULL;
7602 char *scriptname = NULL;
7603 Script *tempscript = NULL;
7604 s_panel_filenames panel_filenames;
7606 unload_level();
7608 printf("Level Loading: '%s'\n", filename);
7610 if(isLoadingScreenTypeBg(loadingbg[1].set)) {
7611 if(custBkgrds) {
7612 strcpy(string, custBkgrds);
7613 strcat(string, "loading2");
7614 load_background(string, 0);
7615 } else {
7616 load_cached_background("data/bgs/loading2", 0);
7618 clearscreen(vscreen);
7619 spriteq_clear();
7620 standard_palette(1);
7623 if(isLoadingScreenTypeBar(loadingbg[1].set)) {
7624 lifebar_colors();
7625 init_colourtable();
7628 update_loading(&loadingbg[1], -1, 1); // initialize the update screen
7630 memset(&next, 0, sizeof(s_spawn_entry));
7632 level = calloc(1, sizeof(s_level));
7633 if(!level) {
7634 eoom:
7635 errormessage = (char*) E_OUT_OF_MEMORY;
7636 goto lCleanup;
7638 level->name = strdup(filename);
7640 if(!level->name)
7641 goto eoom;
7643 if(buffer_pakfile(filename, &buf, &size) != 1) {
7644 errormessage = "Unable to load level file!";
7645 goto lCleanup;
7648 level->settime = 100; // Feb 25, 2005 - Default time limit set to 100
7649 level->nospecial = 0; // Default set to specials can be used during bonus levels
7650 level->nohurt = 0; // Default set to players can hurt each other during bonus levels
7651 level->nohit = 0; // Default able to hit the other player
7652 level->spawn[0][2] = level->spawn[1][2] = level->spawn[2][2] = level->spawn[3][2] = 300; // Set the default spawn a to 300
7653 level->setweap = 0;
7654 level->maxtossspeed = 100;
7655 level->maxfallspeed = -6;
7656 level->gravity = (float) -0.1;
7657 level->scrolldir = SCROLL_RIGHT;
7658 level->cameraxoffset = 0;
7659 level->camerazoffset = 0;
7660 level->bosses = 0;
7661 blendfx[BLEND_MULTIPLY] = 1;
7662 bgtravelled = 0;
7663 traveltime = 0;
7664 texttime = 0;
7665 nopause = 0;
7666 noscreenshot = 0;
7668 reset_playable_list(1);
7670 // Now interpret the contents of buf line by line
7671 pos = 0;
7672 while(pos < size) {
7673 ParseArgs(&arglist, buf + pos, argbuf);
7674 command = GET_ARG(0);
7675 cmd = getLevelCommand(levelcmdlist, command);
7676 switch (cmd) {
7677 case CMD_LEVEL_LOADINGBG:
7678 load_background(GET_ARG(1), 0);
7679 errormessage =
7680 fill_s_loadingbar(&bgPosi, GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4),
7681 GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8),
7682 GET_INT_ARG(9));
7683 if(errormessage)
7684 goto lCleanup;
7685 standard_palette(1);
7686 lifebar_colors();
7687 init_colourtable();
7688 update_loading(&bgPosi, -1, 1); // initialize the update screen
7689 break;
7690 case CMD_LEVEL_MUSICFADE:
7691 memset(&next, 0, sizeof(s_spawn_entry));
7692 next.musicfade = GET_FLOAT_ARG(1);
7693 break;
7694 case CMD_LEVEL_MUSIC:
7695 value = GET_ARG(1);
7696 strncpy(string, value, 128);
7697 musicOffset = atol(GET_ARG(2));
7698 if(loadingmusic) {
7699 music(string, 1, musicOffset);
7700 musicPath[0] = 0;
7701 } else {
7702 oldpos = pos;
7703 // Go to next line
7704 pos += getNewLineStart(buf + pos);
7705 #define GET_ARG2(z) arglist2.count > z ? arglist2.args[z] : ""
7706 if(pos < size) {
7707 ParseArgs(&arglist2, buf + pos, argbuf2);
7708 command = GET_ARG2(0);
7709 cmd2 = getLevelCommand(levelcmdlist, command);
7710 } else
7711 cmd2 = (levelCommands) 0;
7713 if(cmd2 == CMD_LEVEL_AT) {
7714 if(next.musicfade == 0)
7715 memset(&next, 0, sizeof(s_spawn_entry));
7716 strncpy(next.music, string, 128);
7717 next.musicoffset = musicOffset;
7718 } else {
7719 strncpy(musicPath, string, 128);
7721 pos = oldpos;
7722 #undef GET_ARG2
7724 break;
7725 case CMD_LEVEL_ALLOWSELECT:
7726 load_playable_list(buf + pos);
7727 break;
7728 case CMD_LEVEL_LOAD:
7729 #ifdef DEBUG
7730 printf("load_level: load %s, %s\n", GET_ARG(1), filename);
7731 #endif
7732 tempmodel = findmodel(GET_ARG(1));
7733 if(!tempmodel)
7734 load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2));
7735 else
7736 update_model_loadflag(tempmodel, GET_INT_ARG(2));
7737 break;
7738 case CMD_LEVEL_BACKGROUND:
7739 value = GET_ARG(1);
7740 strncpy(bgPath, value, sizeof(bgPath));
7741 level->bglayers[0].type = bg_screen;
7742 level->bglayers[0].bgspeedratio = 1;
7744 level->bglayers[0].xratio = GET_FLOAT_ARG(2); // x ratio
7745 level->bglayers[0].zratio = GET_FLOAT_ARG(3); // z ratio
7746 level->bglayers[0].xoffset = GET_INT_ARG(4); // x start
7747 level->bglayers[0].zoffset = GET_INT_ARG(5); // z start
7748 level->bglayers[0].xspacing = GET_INT_ARG(6); // x spacing
7749 level->bglayers[0].zspacing = GET_INT_ARG(7); // z spacing
7750 level->bglayers[0].xrepeat = GET_INT_ARG(8); // x repeat
7751 level->bglayers[0].zrepeat = GET_INT_ARG(9); // z repeat
7752 // unused
7753 level->bglayers[0].transparency = GET_INT_ARG(10); // transparency
7754 level->bglayers[0].alpha = GET_INT_ARG(11); // alpha
7755 level->bglayers[0].watermode = GET_INT_ARG(12); // amplitude
7756 level->bglayers[0].amplitude = GET_INT_ARG(13); // amplitude
7757 level->bglayers[0].wavelength = GET_INT_ARG(14); // wavelength
7758 level->bglayers[0].wavespeed = GET_FLOAT_ARG(15); // waterspeed
7759 level->bglayers[0].enabled = 1; // enabled
7761 if((value = GET_ARG(2))[0] == 0)
7762 level->bglayers[0].xratio = 0.5;
7763 if((value = GET_ARG(3))[0] == 0)
7764 level->bglayers[0].zratio = 0.5;
7766 if((value = GET_ARG(8))[0] == 0)
7767 level->bglayers[0].xrepeat = 5000;
7768 if((value = GET_ARG(9))[0] == 0)
7769 level->bglayers[0].zrepeat = 5000;
7771 if(level->numbglayers == 0)
7772 level->numbglayers = 1;
7773 break;
7774 case CMD_LEVEL_BGLAYER:
7775 if(level->numbglayers >= LEVEL_MAX_BGLAYERS) {
7776 errormessage = "Too many bg layers in level (check LEVEL_MAX_BGLAYERS)!";
7777 goto lCleanup;
7779 if(level->numbglayers == 0)
7780 level->numbglayers = 1; // reserve for background
7782 level->bglayers[level->numbglayers].xratio = GET_FLOAT_ARG(2); // x ratio
7783 level->bglayers[level->numbglayers].zratio = GET_FLOAT_ARG(3); // z ratio
7784 level->bglayers[level->numbglayers].xoffset = GET_INT_ARG(4); // x start
7785 level->bglayers[level->numbglayers].zoffset = GET_INT_ARG(5); // z start
7786 level->bglayers[level->numbglayers].xspacing = GET_INT_ARG(6); // x spacing
7787 level->bglayers[level->numbglayers].zspacing = GET_INT_ARG(7); // z spacing
7788 level->bglayers[level->numbglayers].xrepeat = GET_INT_ARG(8); // x repeat
7789 level->bglayers[level->numbglayers].zrepeat = GET_INT_ARG(9); // z repeat
7790 level->bglayers[level->numbglayers].transparency = GET_INT_ARG(10); // transparency
7791 level->bglayers[level->numbglayers].alpha = GET_INT_ARG(11); // alpha
7792 level->bglayers[level->numbglayers].watermode = GET_INT_ARG(12); // amplitude
7793 level->bglayers[level->numbglayers].amplitude = GET_INT_ARG(13); // amplitude
7794 level->bglayers[level->numbglayers].wavelength = GET_INT_ARG(14); // wavelength
7795 level->bglayers[level->numbglayers].wavespeed = GET_FLOAT_ARG(15); // waterspeed
7796 level->bglayers[level->numbglayers].bgspeedratio = GET_FLOAT_ARG(16); // moving
7797 level->bglayers[level->numbglayers].enabled = 1; // enabled
7799 if((value = GET_ARG(2))[0] == 0)
7800 level->bglayers[level->numbglayers].xratio = 0.5;
7801 if((value = GET_ARG(3))[0] == 0)
7802 level->bglayers[level->numbglayers].zratio = 0.5;
7804 if((value = GET_ARG(8))[0] == 0)
7805 level->bglayers[level->numbglayers].xrepeat = 5000; // close enough to infinite, lol
7806 if((value = GET_ARG(9))[0] == 0)
7807 level->bglayers[level->numbglayers].zrepeat = 5000;
7809 if(blendfx_is_set == 0 && level->bglayers[level->numbglayers].alpha)
7810 blendfx[level->bglayers[level->numbglayers].alpha - 1] = 1;
7812 load_bglayer(GET_ARG(1), level->numbglayers);
7813 level->numbglayers++;
7814 break;
7815 case CMD_LEVEL_FGLAYER:
7816 if(level->numfglayers >= LEVEL_MAX_FGLAYERS) {
7817 errormessage = "Too many bg layers in level (check LEVEL_MAX_FGLAYERS)!";
7818 goto lCleanup;
7821 level->fglayers[level->numfglayers].z = GET_INT_ARG(2); // z
7822 level->fglayers[level->numfglayers].xratio = GET_FLOAT_ARG(3); // x ratio
7823 level->fglayers[level->numfglayers].zratio = GET_FLOAT_ARG(4); // z ratio
7824 level->fglayers[level->numfglayers].xoffset = GET_INT_ARG(5); // x start
7825 level->fglayers[level->numfglayers].zoffset = GET_INT_ARG(6); // z start
7826 level->fglayers[level->numfglayers].xspacing = GET_INT_ARG(7); // x spacing
7827 level->fglayers[level->numfglayers].zspacing = GET_INT_ARG(8); // z spacing
7828 level->fglayers[level->numfglayers].xrepeat = GET_INT_ARG(9); // x repeat
7830 level->fglayers[level->numfglayers].zrepeat = GET_INT_ARG(10); // z repeat
7831 level->fglayers[level->numfglayers].transparency = GET_INT_ARG(11); // transparency
7832 level->fglayers[level->numfglayers].alpha = GET_INT_ARG(12); // alpha
7833 level->fglayers[level->numfglayers].watermode = GET_INT_ARG(13); // amplitude
7834 level->fglayers[level->numfglayers].amplitude = GET_INT_ARG(14); // amplitude
7835 level->fglayers[level->numfglayers].wavelength = GET_INT_ARG(15); // wavelength
7836 level->fglayers[level->numfglayers].wavespeed = GET_FLOAT_ARG(16); // waterspeed
7837 level->fglayers[level->numfglayers].bgspeedratio = GET_FLOAT_ARG(17); // moving
7838 level->fglayers[level->numfglayers].enabled = 1;
7840 if((value = GET_ARG(2))[0] == 0)
7841 level->fglayers[level->numfglayers].xratio = 1.5;
7842 if((value = GET_ARG(3))[0] == 0)
7843 level->fglayers[level->numfglayers].zratio = 1.5;
7845 if((value = GET_ARG(8))[0] == 0)
7846 level->fglayers[level->numfglayers].xrepeat = 5000; // close enough to infinite, lol
7847 if((value = GET_ARG(9))[0] == 0)
7848 level->fglayers[level->numfglayers].zrepeat = 5000;
7850 if(blendfx_is_set == 0 && level->fglayers[level->numfglayers].alpha)
7851 blendfx[level->fglayers[level->numfglayers].alpha - 1] = 1;
7853 load_fglayer(GET_ARG(1), level->numfglayers);
7854 level->numfglayers++;
7855 break;
7856 case CMD_LEVEL_WATER:
7857 load_texture(GET_ARG(1));
7858 i = GET_INT_ARG(2);
7859 if(i < 1)
7860 i = 1;
7861 texture_set_wave((float) i);
7862 break;
7863 case CMD_LEVEL_DIRECTION:
7864 value = GET_ARG(1);
7865 if(stricmp(value, "up") == 0)
7866 level->scrolldir = SCROLL_UP;
7867 else if(stricmp(value, "down") == 0)
7868 level->scrolldir = SCROLL_DOWN;
7869 else if(stricmp(value, "left") == 0)
7870 level->scrolldir = SCROLL_LEFT;
7871 else if(stricmp(value, "both") == 0 || stricmp(value, "rightleft") == 0)
7872 level->scrolldir = SCROLL_BOTH;
7873 else if(stricmp(value, "leftright") == 0)
7874 level->scrolldir = SCROLL_LEFTRIGHT;
7875 else if(stricmp(value, "right") == 0)
7876 level->scrolldir = SCROLL_RIGHT;
7877 else if(stricmp(value, "in") == 0)
7878 level->scrolldir = SCROLL_INWARD;
7879 else if(stricmp(value, "out") == 0)
7880 level->scrolldir = SCROLL_OUTWARD;
7881 else if(stricmp(value, "inout") == 0)
7882 level->scrolldir = SCROLL_INOUT;
7883 else if(stricmp(value, "outin") == 0)
7884 level->scrolldir = SCROLL_OUTIN;
7885 break;
7886 case CMD_LEVEL_FACING:
7887 level->facing = GET_INT_ARG(1);
7888 break;
7889 case CMD_LEVEL_ROCK:
7890 level->rocking = GET_INT_ARG(1);
7891 break;
7892 case CMD_LEVEL_BGSPEED:
7893 level->bgspeed = GET_FLOAT_ARG(1);
7894 if(GET_INT_ARG(2))
7895 level->bgspeed *= -1;
7896 break;
7897 case CMD_LEVEL_MIRROR:
7898 level->mirror = GET_INT_ARG(1);
7899 break;
7900 case CMD_LEVEL_BOSSMUSIC:
7901 strncpy(level->bossmusic, GET_ARG(1), 255);
7902 level->bossmusic_offset = atol(GET_ARG(2));
7903 break;
7904 case CMD_LEVEL_NOPAUSE:
7905 nopause = GET_INT_ARG(1);
7906 break;
7907 case CMD_LEVEL_NOSCREENSHOT:
7908 noscreenshot = GET_INT_ARG(1);
7909 break;
7910 case CMD_LEVEL_SETTIME:
7911 // If settime is found, overwrite the default 100 for time limit
7912 level->settime = GET_INT_ARG(1);
7913 if(level->settime > 100 || level->settime < 0)
7914 level->settime = 100;
7915 // Feb 25, 2005 - Time limit loaded from individual .txt file
7916 break;
7917 case CMD_LEVEL_SETWEAP:
7918 // Specify a weapon for each level
7919 level->setweap = GET_INT_ARG(1);
7920 break;
7921 case CMD_LEVEL_NOTIME:
7922 // Flag to if the time should be displayed 1 = no, else yes
7923 level->notime = GET_INT_ARG(1);
7924 break;
7925 case CMD_LEVEL_NORESET:
7926 // Flag to if the time should be reset when players respawn 1 = no, else yes
7927 level->noreset = GET_INT_ARG(1);
7928 break;
7929 case CMD_LEVEL_NOSLOW:
7930 // If set, level will not slow down when bosses are defeated
7931 level->noslow = GET_INT_ARG(1);
7932 break;
7933 case CMD_LEVEL_TYPE:
7934 level->type = GET_INT_ARG(1); // Level type - 1 = bonus, else regular
7935 level->nospecial = GET_INT_ARG(2); // Can use specials during bonus levels (default 0 - yes)
7936 level->nohurt = GET_INT_ARG(3); // Can hurt other players during bonus levels (default 0 - yes)
7937 break;
7938 case CMD_LEVEL_NOHIT:
7939 level->nohit = GET_INT_ARG(1);
7940 break;
7941 case CMD_LEVEL_GRAVITY:
7942 level->gravity = GET_FLOAT_ARG(1);
7943 level->gravity /= 100;
7944 break;
7945 case CMD_LEVEL_MAXFALLSPEED:
7946 level->maxfallspeed = GET_FLOAT_ARG(1);
7947 level->maxfallspeed /= 10;
7948 break;
7949 case CMD_LEVEL_MAXTOSSSPEED:
7950 level->maxtossspeed = GET_FLOAT_ARG(1);
7951 level->maxtossspeed /= 10;
7952 break;
7953 case CMD_LEVEL_CAMERATYPE:
7954 cameratype = GET_INT_ARG(1);
7955 break;
7956 case CMD_LEVEL_CAMERAOFFSET:
7957 level->cameraxoffset = GET_INT_ARG(1);
7958 level->camerazoffset = GET_INT_ARG(2);
7959 break;
7960 case CMD_LEVEL_SPAWN1:
7961 case CMD_LEVEL_SPAWN2:
7962 case CMD_LEVEL_SPAWN3:
7963 case CMD_LEVEL_SPAWN4:
7964 switch (cmd) {
7965 case CMD_LEVEL_SPAWN1:
7966 i = 0;
7967 break;
7968 case CMD_LEVEL_SPAWN2:
7969 i = 1;
7970 break;
7971 case CMD_LEVEL_SPAWN3:
7972 i = 2;
7973 break;
7974 case CMD_LEVEL_SPAWN4:
7975 i = 3;
7976 break;
7977 default:
7978 assert(0);
7980 level->spawn[i][0] = GET_INT_ARG(1);
7981 level->spawn[i][1] = GET_INT_ARG(2);
7982 level->spawn[i][2] = GET_INT_ARG(3);
7984 if(level->spawn[i][1] > 232 || level->spawn[i][1] < 0)
7985 level->spawn[i][1] = 232;
7986 if(level->spawn[i][2] < 0)
7987 level->spawn[i][2] = 300;
7988 break;
7989 case CMD_LEVEL_FRONTPANEL:
7990 value = GET_ARG(1);
7991 if(!loadfrontpanel(value))
7992 shutdown(1, "Unable to load '%s'!", value);
7993 break;
7994 case CMD_LEVEL_PANEL:
7995 panel_filenames.sprite_normal = GET_ARG(1);
7996 panel_filenames.sprite_neon = GET_ARG(2);
7997 panel_filenames.sprite_screen = GET_ARG(3);
7998 if(!loadpanel(&panel_filenames)) {
7999 printf("loadpanel :%s :%s :%s failed\n", GET_ARG(1), GET_ARG(2), GET_ARG(3));
8000 errormessage = "Panel load error!";
8001 goto lCleanup;
8003 break;
8004 case CMD_LEVEL_STAGENUMBER:
8005 current_stage = GET_INT_ARG(1);
8006 break;
8007 case CMD_LEVEL_ORDER:
8008 // Append to order
8009 if(panels_loaded < 1) {
8010 errormessage = "You must load the panels before entering the level layout!";
8011 goto lCleanup;
8014 value = GET_ARG(1);
8015 i = 0;
8016 while(value[i] && level->numpanels < LEVEL_MAX_PANELS) {
8017 j = value[i];
8018 // WTF ?
8019 if(j >= 'A' && j <= 'Z')
8020 j -= 'A';
8021 else if(j >= 'a' && j <= 'z')
8022 j -= 'a';
8023 else {
8024 errormessage = "Illegal character in panel order!";
8025 goto lCleanup;
8028 if(j >= panels_loaded) {
8029 errormessage =
8030 "Illegal panel index, index is bigger than number of loaded panels.";
8031 goto lCleanup;
8034 level->order[level->numpanels] = j;
8035 level->numpanels++;
8036 i++;
8038 break;
8039 case CMD_LEVEL_HOLE:
8040 // TODO: apparently the first parameter can be a filename, so adjust value assignment accordingly if it is a string
8041 value = GET_ARG(1); // ltb 1-18-05 adjustable hole sprites
8043 if(holesprite < 0) {
8044 if(testpackfile(value, packfile) >= 0)
8045 holesprite = loadsprite(value, 0, 0, pixelformat); // ltb 1-18-05 load new hole sprite
8046 else
8047 holesprite = loadsprite("data/sprites/hole", 0, 0, pixelformat); // ltb 1-18-05 no new sprite load the default
8050 if(level->numholes >= LEVEL_MAX_HOLES) {
8051 errormessage = "Too many holes in level (check LEVEL_MAX_HOLES)!";
8052 goto lCleanup;
8054 for(i = 0; i < sizeof(s_hole) / sizeof(float); i++) {
8055 ((float*) &level->holes[level->numholes])[i] = GET_FLOAT_ARG(i + 1);
8056 if(! ((float*) &level->holes[level->numholes])[i])
8057 ((float*) &level->holes[level->numholes])[i] = ((float*) &default_hole_coords)[i];
8059 level->numholes++;
8060 break;
8061 case CMD_LEVEL_WALL:
8062 if(level->numwalls >= LEVEL_MAX_WALLS) {
8063 errormessage = "Too many walls in level (check LEVEL_MAX_WALLS)!";
8064 goto lCleanup;
8066 for(i = 0; i < sizeof(s_wall) / sizeof(float); i++) {
8067 ((float*) &level->walls[level->numwalls])[i] = GET_FLOAT_ARG(i + 1);
8069 level->numwalls++;
8070 break;
8071 case CMD_LEVEL_PALETTE:
8072 if(level->numpalettes >= LEVEL_MAX_PALETTES) {
8073 errormessage = "Too many palettes in level (check LEVEL_MAX_PALETTES)!";
8074 goto lCleanup;
8076 for(i = 0; i < MAX_BLENDINGS; i++)
8077 usemap[i] = GET_INT_ARG(i + 2);
8078 if(!load_palette(level->palettes[level->numpalettes], GET_ARG(1)) ||
8079 !create_blending_tables(level->palettes[level->numpalettes],
8080 level->blendings[level->numpalettes], usemap)) {
8081 errormessage = (char*) E_OUT_OF_MEMORY;
8082 goto lCleanup;
8084 level->numpalettes++;
8085 break;
8086 case CMD_LEVEL_UPDATESCRIPT:
8087 case CMD_LEVEL_UPDATEDSCRIPT:
8088 case CMD_LEVEL_KEYSCRIPT:
8089 case CMD_LEVEL_LEVELSCRIPT:
8090 case CMD_LEVEL_ENDLEVELSCRIPT:
8091 switch (cmd) {
8092 case CMD_LEVEL_UPDATESCRIPT:
8093 tempscript = &(level->update_script);
8094 scriptname = "levelupdatescript";
8095 break;
8096 case CMD_LEVEL_UPDATEDSCRIPT:
8097 tempscript = &(level->updated_script);
8098 scriptname = "levelupdatedscript";
8099 break;
8100 case CMD_LEVEL_KEYSCRIPT:
8101 tempscript = &(level->key_script);
8102 scriptname = "levelkeyscript";
8103 break;
8104 case CMD_LEVEL_LEVELSCRIPT:
8105 tempscript = &(level->level_script);
8106 scriptname = command;
8107 break;
8108 case CMD_LEVEL_ENDLEVELSCRIPT:
8109 tempscript = &(level->endlevel_script);
8110 scriptname = command;
8111 break;
8112 default:
8113 assert(0);
8116 value = GET_ARG(1);
8117 if(!Script_IsInitialized(tempscript))
8118 Script_Init(tempscript, scriptname, 1);
8119 else {
8120 errormessage = "Multiple level script!";
8121 goto lCleanup;
8123 if(load_script(tempscript, value))
8124 Script_Compile(tempscript);
8125 else {
8126 errormessage = "Failed loading script!";
8127 goto lCleanup;
8129 break;
8130 case CMD_LEVEL_BLOCKED:
8131 level->exit_blocked = GET_INT_ARG(1);
8132 break;
8133 case CMD_LEVEL_ENDHOLE:
8134 level->exit_hole = GET_INT_ARG(1);
8135 break;
8136 case CMD_LEVEL_WAIT:
8137 // Clear spawn thing, set wait state instead
8138 memset(&next, 0, sizeof(s_spawn_entry));
8139 next.wait = 1;
8140 break;
8141 case CMD_LEVEL_NOJOIN:
8142 case CMD_LEVEL_CANJOIN:
8143 // Clear spawn thing, set nojoin state instead
8144 memset(&next, 0, sizeof(s_spawn_entry));
8145 next.nojoin = 1;
8146 break;
8147 case CMD_LEVEL_SHADOWCOLOR:
8148 memset(&next, 0, sizeof(s_spawn_entry));
8149 next.shadowcolor = GET_INT_ARG(1);
8150 break;
8151 case CMD_LEVEL_SHADOWALPHA:
8152 memset(&next, 0, sizeof(s_spawn_entry));
8153 next.shadowalpha = GET_INT_ARG(1);
8154 if(blendfx_is_set == 0 && next.shadowalpha > 0)
8155 blendfx[next.shadowalpha - 1] = 1;
8156 break;
8157 case CMD_LEVEL_LIGHT:
8158 memset(&next, 0, sizeof(s_spawn_entry));
8159 next.light[0] = GET_INT_ARG(1);
8160 next.light[1] = GET_INT_ARG(2);
8161 if(next.light[1] == 0)
8162 next.light[1] = 64;
8163 break;
8164 case CMD_LEVEL_SCROLLZ:
8165 case CMD_LEVEL_SCROLLX:
8166 // now z scroll can be limited by this
8167 // if the level is vertical, use scrollx, only different in name ..., but makes more sense
8168 memset(&next, 0, sizeof(s_spawn_entry));
8169 next.scrollminz = GET_INT_ARG(1);
8170 next.scrollmaxz = GET_INT_ARG(2);
8171 if(next.scrollminz <= 0)
8172 next.scrollminz = 4;
8173 if(next.scrollmaxz <= 0)
8174 next.scrollmaxz = 4;
8175 break;
8176 case CMD_LEVEL_BLOCKADE:
8177 // now x scroll can be limited by this
8178 memset(&next, 0, sizeof(s_spawn_entry));
8179 next.blockade = GET_INT_ARG(1);
8180 if(next.blockade == 0)
8181 next.blockade = -1;
8182 break;
8183 case CMD_LEVEL_SETPALETTE:
8184 // change system palette
8185 memset(&next, 0, sizeof(s_spawn_entry));
8186 next.palette = GET_INT_ARG(1);
8187 break;
8188 case CMD_LEVEL_GROUP:
8189 // Clear spawn thing, set group instead
8190 memset(&next, 0, sizeof(s_spawn_entry));
8191 next.groupmin = GET_INT_ARG(1);
8192 next.groupmax = GET_INT_ARG(2);
8193 if(next.groupmax < 1)
8194 next.groupmax = 1;
8195 if(next.groupmin < 1)
8196 next.groupmin = 100;
8197 break;
8198 case CMD_LEVEL_SPAWN:
8199 // Back to defaults
8200 next.spawnplayer_count = 0;
8201 memset(&next, 0, sizeof(s_spawn_entry));
8202 next.index = next.itemindex = next.weaponindex = -1;
8203 // Name of entry to be spawned
8204 // Load model (if not loaded already)
8205 cached_model = findmodel(GET_ARG(1));
8206 #ifdef DEBUG
8207 printf("load_level: spawn %s, %s, cached: %p\n", GET_ARG(1), filename, cached_model);
8208 #endif
8209 if(cached_model)
8210 tempmodel = cached_model;
8211 else
8212 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
8213 if(tempmodel) {
8214 next.name = tempmodel->name;
8215 next.index = get_cached_model_index(next.name);
8216 next.spawntype = 1; //2011_03_23, DC; Spawntype 1 (level spawn).
8217 crlf = 1;
8219 break;
8220 case CMD_LEVEL_2PSPAWN:
8221 // Entity only for 2p game
8222 next.spawnplayer_count = 1;
8223 break;
8224 case CMD_LEVEL_3PSPAWN:
8225 // Entity only for 3p game
8226 next.spawnplayer_count = 2;
8227 break;
8228 case CMD_LEVEL_4PSPAWN:
8229 // Entity only for 4p game
8230 next.spawnplayer_count = 3;
8231 break;
8232 case CMD_LEVEL_BOSS:
8233 next.boss = GET_INT_ARG(1);
8234 level->bosses += next.boss ? 1 : 0;
8235 break;
8236 case CMD_LEVEL_FLIP:
8237 next.flip = GET_INT_ARG(1);
8238 break;
8239 case CMD_LEVEL_HEALTH:
8240 next.health[0] = next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1);
8241 break;
8242 case CMD_LEVEL_2PHEALTH:
8243 // Health the spawned entity will have if 2 people are playing
8244 next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1);
8245 break;
8246 case CMD_LEVEL_3PHEALTH:
8247 // Health the spawned entity will have if 2 people are playing
8248 next.health[2] = next.health[3] = GET_INT_ARG(1); //4player
8249 break;
8250 case CMD_LEVEL_4PHEALTH:
8251 // Health the spawned entity will have if 2 people are playing
8252 next.health[3] = GET_INT_ARG(1); //4player
8253 break;
8254 case CMD_LEVEL_MP:
8255 // mp values to put max mp for player by tails
8256 next.mp = GET_INT_ARG(1);
8257 break;
8258 case CMD_LEVEL_SCORE:
8259 // So score can be overriden in the levels .txt file
8260 next.score = GET_INT_ARG(1);
8261 if(next.score < 0)
8262 next.score = 0; // So negative values cannot be added
8263 next.multiple = GET_INT_ARG(2);
8264 if(next.multiple < 0)
8265 next.multiple = 0; // So negative values cannot be added
8266 break;
8267 case CMD_LEVEL_NOLIFE:
8268 // Flag to determine if entity life is shown when hit
8269 next.nolife = GET_INT_ARG(1);
8270 break;
8271 case CMD_LEVEL_ALIAS:
8272 // Alias (name displayed) of entry to be spawned
8273 strncpy(next.alias, GET_ARG(1), MAX_NAME_LEN);
8274 break;
8275 case CMD_LEVEL_MAP:
8276 // Colourmap for new entry
8277 next.colourmap = GET_INT_ARG(1);
8278 break;
8279 case CMD_LEVEL_ALPHA:
8280 // Item to be contained by new entry
8281 next.alpha = GET_INT_ARG(1);
8282 if(blendfx_is_set == 0 && next.alpha)
8283 blendfx[next.alpha - 1] = 1;
8284 break;
8285 case CMD_LEVEL_DYING:
8286 // Used to store which remake corresponds with the dying flash
8287 next.dying = GET_INT_ARG(1);
8288 next.per1 = GET_INT_ARG(2);
8289 next.per2 = GET_INT_ARG(3);
8290 break;
8291 case CMD_LEVEL_ITEM:
8292 case CMD_LEVEL_2PITEM:
8293 case CMD_LEVEL_3PITEM:
8294 case CMD_LEVEL_4PITEM:
8295 switch (cmd) {
8296 // Item to be contained by new entry
8297 case CMD_LEVEL_ITEM:
8298 next.itemplayer_count = 0;
8299 break;
8300 case CMD_LEVEL_2PITEM:
8301 next.itemplayer_count = 1;
8302 break;
8303 case CMD_LEVEL_3PITEM:
8304 next.itemplayer_count = 2;
8305 break;
8306 case CMD_LEVEL_4PITEM:
8307 next.itemplayer_count = 3;
8308 break;
8309 default:
8310 assert(0);
8312 // Load model (if not loaded already)
8313 cached_model = findmodel(GET_ARG(1));
8314 if(cached_model)
8315 tempmodel = cached_model;
8316 else
8317 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
8318 if(tempmodel) {
8319 next.item = tempmodel->name;
8320 next.itemindex = get_cached_model_index(next.item);
8322 break;
8323 case CMD_LEVEL_ITEMMAP:
8324 next.itemmap = GET_INT_ARG(1);
8325 break;
8326 case CMD_LEVEL_ITEMHEALTH:
8327 next.itemhealth = GET_INT_ARG(1);
8328 break;
8329 case CMD_LEVEL_ITEMALIAS:
8330 strncpy(next.itemalias, GET_ARG(1), MAX_NAME_LEN);
8331 break;
8332 case CMD_LEVEL_WEAPON:
8333 //spawn with a weapon 2007-2-12 by UTunnels
8334 // Load model (if not loaded already)
8335 cached_model = findmodel(GET_ARG(1));
8336 if(cached_model)
8337 tempmodel = cached_model;
8338 else
8339 tempmodel = load_cached_model(GET_ARG(1), filename, 0);
8340 if(tempmodel) {
8341 next.weapon = tempmodel->name;
8342 next.weaponindex = get_cached_model_index(next.weapon);
8344 break;
8345 case CMD_LEVEL_AGGRESSION:
8346 // Aggression can be set per spawn.
8347 next.aggression = next.aggression + GET_INT_ARG(1);
8348 break;
8349 case CMD_LEVEL_CREDIT:
8350 next.credit = GET_INT_ARG(1);
8351 break;
8352 case CMD_LEVEL_ITEMTRANS:
8353 case CMD_LEVEL_ITEMALPHA:
8354 next.itemtrans = GET_INT_ARG(1);
8355 break;
8356 case CMD_LEVEL_COORDS:
8357 next.x = GET_FLOAT_ARG(1);
8358 next.z = GET_FLOAT_ARG(2);
8359 next.a = GET_FLOAT_ARG(3);
8360 break;
8361 case CMD_LEVEL_SPAWNSCRIPT:
8362 errormessage = llHandleCommandSpawnscript(&arglist, &next);
8363 if(errormessage)
8364 goto lCleanup;
8365 break;
8366 case CMD_LEVEL_AT:
8367 // Place entry on queue
8368 next.at = GET_INT_ARG(1);
8370 if(level->numspawns >= LEVEL_MAX_SPAWNS) {
8371 errormessage = "too many spawn entries (see LEVEL_MAX_SPAWNS)";
8372 goto lCleanup;
8375 memcpy(&level->spawnpoints[level->numspawns], &next, sizeof(s_spawn_entry));
8376 level->numspawns++;
8378 // And clear...
8379 memset(&next, 0, sizeof(s_spawn_entry));
8380 break;
8381 default:
8382 if(command && command[0])
8383 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
8386 // Go to next line
8387 pos += getNewLineStart(buf + pos);
8388 line++;
8390 if(isLoadingScreenTypeBar(bgPosi.set) || isLoadingScreenTypeBg(bgPosi.set))
8391 update_loading(&bgPosi, pos, size);
8392 //update_loading(bgPosi[0]+videomodes.hShift, bgPosi[1]+videomodes.vShift, bgPosi[2], bgPosi[3]+videomodes.hShift, bgPosi[4]+videomodes.vShift, pos, size, bgPosi[5]);
8393 else
8394 update_loading(&loadingbg[1], pos, size);
8397 if(level->numpanels < 1) {
8398 errormessage = "Level error: level has no panels";
8399 goto lCleanup;
8402 if(bgPath[0]) {
8403 clearscreen(vscreen);
8404 spriteq_clear();
8405 load_background(bgPath, 1);
8406 level->bglayers[0].screen = background;
8407 level->bglayers[0].width = background->width;
8408 //if(!level->bglayers[0].xoffset && level->bglayers[0].xrepeat<5000)level->bglayers[0].xoffset=-background->width;
8409 level->bglayers[0].height = background->height;
8410 } else {
8411 if(level->numbglayers > 1)
8412 level->bglayers[0] = level->bglayers[--level->numbglayers];
8413 else
8414 level->numbglayers = 0;
8416 if(background)
8417 unload_background();
8420 if(pixelformat == PIXEL_x8) {
8421 if(level->numbglayers > 0)
8422 bgbuffer = allocscreen(videomodes.hRes, videomodes.vRes, screenformat);
8424 bgbuffer_updated = 0;
8425 if(musicPath[0])
8426 music(musicPath, 1, musicOffset);
8428 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
8429 level->width = level->numpanels * panel_width;
8431 if(level->scrolldir & SCROLL_LEFT)
8432 advancex = (float) (level->width - videomodes.hRes);
8433 else if(level->scrolldir & SCROLL_INWARD)
8434 advancey = (float) (panel_height - videomodes.vRes);
8436 if(crlf)
8437 printf("\n");
8438 printf("Level Loaded: '%s'\n", level->name);
8440 lCleanup:
8441 freeAndNull((void**) &buf);
8443 if(errormessage)
8444 shutdown(1, "ERROR: load_level, file %s, line %d, message: %s", filename, line, errormessage);
8451 /////////////////////////////////////////////////////////////////////////////
8452 // Status //
8453 /////////////////////////////////////////////////////////////////////////////
8454 void bar(int x, int y, int value, int maxvalue, s_barstatus * pstatus) {
8455 int max = 100, len, alphabg = 0, bgindex, colourindex;
8456 int forex, forey, forew, foreh, bkw, bkh;
8458 x += pstatus->offsetx;
8459 y += pstatus->offsety;
8461 if(pstatus->orientation == horizontalbar)
8462 max = pstatus->sizex;
8463 else if(pstatus->orientation == verticalbar)
8464 max = pstatus->sizey;
8465 else
8466 return;
8468 if(value > maxvalue)
8469 value = maxvalue;
8471 if(pstatus->type == valuebar) {
8472 if(max > maxvalue)
8473 max = maxvalue;
8474 if(colorbars) {
8475 if(value <= max / 4) {
8476 bgindex = 0;
8477 colourindex = 1;
8478 } else if(value <= max / 2) {
8479 bgindex = 0;
8480 colourindex = 2;
8481 } else if(value <= max) {
8482 bgindex = 0;
8483 colourindex = 3;
8484 } else {
8485 colourindex = value / (max + 1) + 3;
8486 bgindex = colourindex - 1;
8488 if(colourindex > 10)
8489 colourindex = bgindex = 10;
8490 } else {
8491 colourindex = 2;
8492 bgindex = value > max ? 5 : 1;
8495 len = value % max;
8496 if(!len && value)
8497 len = max;
8498 alphabg = value > max ? 0 : (BLEND_MULTIPLY + 1);
8499 } else if(pstatus->type == percentagebar) {
8500 colourindex = colorbars ? (value * 5 / maxvalue + 1) : 2;
8501 bgindex = colorbars ? 8 : 1;
8502 len = value * max / maxvalue;
8503 if(!len && value)
8504 len = 1;
8505 alphabg = BLEND_MULTIPLY + 1;
8506 } else
8507 return;
8509 if(pstatus->orientation == horizontalbar) {
8510 forex = pstatus->direction ? (x + max - len) : x;
8511 forey = y;
8512 forew = len;
8513 bkw = max;
8514 bkh = foreh = pstatus->sizey;
8515 } else if(pstatus->orientation == verticalbar) {
8516 forex = x;
8517 forey = pstatus->direction ? y : (y + max - len);
8518 bkw = forew = pstatus->sizex;
8519 foreh = len;
8520 bkh = max;
8521 } else
8522 return;
8524 if(!pstatus->colourtable)
8525 pstatus->colourtable = &color_tables.hp;
8527 spriteq_add_box(x + 1, y + 1, bkw, bkh, HUD_Z + 1 + pstatus->backlayer, (*pstatus->colourtable)[bgindex],
8528 alphabg);
8529 spriteq_add_box(forex + 1, forey + 1, forew, foreh, HUD_Z + 2 + pstatus->barlayer,
8530 (*pstatus->colourtable)[colourindex], 0);
8532 if(pstatus->noborder == 0) {
8533 spriteq_add_line(x, y, x + bkw + 1, y, HUD_Z + 3 + pstatus->borderlayer, colors.white, 0); //Top border.
8534 spriteq_add_line(x, y + bkh + 1, x + bkw + 1, y + bkh + 1, HUD_Z + 3 + pstatus->borderlayer, colors.white, 0); //Bottom border.
8535 spriteq_add_line(x, y + 1, x, y + bkh, HUD_Z + 3 + pstatus->borderlayer, colors.white, 0); //Left border.
8536 spriteq_add_line(x + bkw + 1, y + 1, x + bkw + 1, y + bkh, HUD_Z + 3 + pstatus->borderlayer, colors.white, 0); //Right border.
8537 spriteq_add_line(x, y + bkh + 2, x + bkw + 1, y + bkh + 2, HUD_Z + pstatus->borderlayer, colors.black, 0); //Bottom shadow.
8538 spriteq_add_line(x + bkw + 2, y + 1, x + bkw + 2, y + bkh + 2, HUD_Z + pstatus->borderlayer, colors.black, 0); //Right shadow.
8543 void pausemenu() {
8544 int pauselector = 0;
8545 int quit = 0;
8547 pause = 2;
8548 bothnewkeys = 0;
8549 while(!quit) {
8550 _menutextm(3, -2, 0, "Pause");
8551 _menutextm((pauselector == 0), -1, 0, "Continue");
8552 _menutextm((pauselector == 1), 0, 0, "End Game");
8554 update(1, 0);
8556 if(bothnewkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN)) {
8557 pauselector ^= 1;
8558 sound_play_sample(samples.beep, 0, savedata.effectvol, savedata.effectvol, 100);
8560 if(bothnewkeys & FLAG_START) {
8561 if(pauselector) {
8562 player[0].lives = player[1].lives = player[2].lives = player[3].lives = 0; //4player
8563 endgame = 1;
8565 quit = 1;
8566 sound_pause_music(0);
8567 sound_play_sample(samples.beep2, 0, savedata.effectvol, savedata.effectvol, 100);
8568 pauselector = 0;
8570 if(bothnewkeys & FLAG_ESC) {
8571 quit = 1;
8572 sound_pause_music(0);
8573 sound_play_sample(samples.beep2, 0, savedata.effectvol, savedata.effectvol, 100);
8574 pauselector = 0;
8576 if(bothnewkeys & FLAG_SCREENSHOT) {
8577 pause = 1;
8578 sound_pause_music(1);
8579 sound_play_sample(samples.beep2, 0, savedata.effectvol, savedata.effectvol, 100);
8580 options();
8583 pause = 0;
8584 bothnewkeys = 0;
8585 spriteq_unlock();
8588 unsigned getFPS(void) {
8589 static unsigned lasttick = 0, framerate = 0;
8590 unsigned curtick = timer_gettick();
8591 if(lasttick > curtick)
8592 lasttick = curtick;
8593 framerate = (framerate + (curtick - lasttick)) / 2;
8594 lasttick = curtick;
8595 if(!framerate)
8596 return 0;
8597 #ifdef PSP
8598 return ((10000000 / framerate) + 9) / 10;
8599 #else
8600 return ((10000000 / framerate) + 9) / 10000;
8601 #endif
8604 void predrawstatus() {
8606 int dt;
8607 int tperror = 0;
8608 int icon = 0;
8609 int i, x;
8610 unsigned long tmp;
8612 s_model *model = NULL;
8613 s_drawmethod drawmethod = plainmethod;
8615 if(bgicon >= 0)
8616 spriteq_add_sprite(videomodes.hShift + bgicon_offsets[0], savedata.windowpos + bgicon_offsets[1],
8617 bgicon_offsets[2], bgicon, NULL, 0);
8618 if(olicon >= 0)
8619 spriteq_add_sprite(videomodes.hShift + olicon_offsets[0], savedata.windowpos + olicon_offsets[1],
8620 olicon_offsets[2], olicon, NULL, 0);
8622 for(i = 0; i < maxplayers[current_set]; i++) {
8623 if(player[i].ent) {
8624 tmp = player[i].score; //work around issue on 64bit where sizeof(long) != sizeof(int)
8625 if(!pscore[i][2] && !pscore[i][3] && !pscore[i][4] && !pscore[i][5])
8626 font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1],
8627 pscore[i][6], 0, (scoreformat ? "%s - %09lu" : "%s - %lu"),
8628 (char *) (player[i].ent->name), tmp);
8629 else {
8630 font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1],
8631 pscore[i][6], 0, "%s", player[i].ent->name);
8632 font_printf(videomodes.shiftpos[i] + pscore[i][2], savedata.windowpos + pscore[i][3],
8633 pscore[i][6], 0, "-");
8634 font_printf(videomodes.shiftpos[i] + pscore[i][4], savedata.windowpos + pscore[i][5],
8635 pscore[i][6], 0, (scoreformat ? "%09lu" : "%lu"), tmp);
8638 if(player[i].ent->health <= 0)
8639 icon = player[i].ent->modeldata.icondie;
8640 else if(player[i].ent->inpain)
8641 icon = player[i].ent->modeldata.iconpain;
8642 else if(player[i].ent->getting)
8643 icon = player[i].ent->modeldata.iconget;
8644 else
8645 icon = player[i].ent->modeldata.icon;
8647 if(icon >= 0) {
8648 drawmethod.table = player[i].ent->colourmap;
8649 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0],
8650 savedata.windowpos + picon[i][1], 10000, icon, &drawmethod, 0);
8653 if(player[i].ent->weapent) {
8654 if(player[i].ent->weapent->modeldata.iconw >= 0) {
8655 drawmethod.table = player[i].ent->weapent->colourmap;
8656 spriteq_add_sprite(videomodes.shiftpos[i] + piconw[i][0],
8657 savedata.windowpos + piconw[i][1], 10000,
8658 player[i].ent->weapent->modeldata.iconw, &drawmethod, 0);
8661 if(player[i].ent->weapent->modeldata.typeshot
8662 && player[i].ent->weapent->modeldata.shootnum)
8663 font_printf(videomodes.shiftpos[i] + pshoot[i][0],
8664 savedata.windowpos + pshoot[i][1], pshoot[i][2], 0, "%u",
8665 player[i].ent->weapent->modeldata.shootnum);
8668 if(player[i].ent->modeldata.mp) {
8669 if(player[i].ent->modeldata.iconmp[0] > 0
8670 && (player[i].ent->oldmp >= (player[i].ent->modeldata.mp * .66))) {
8671 drawmethod.table = player[i].ent->colourmap;
8672 spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0],
8673 savedata.windowpos + mpicon[i][1], 10000,
8674 player[i].ent->modeldata.iconmp[0], &drawmethod, 0);
8675 } else if(player[i].ent->modeldata.iconmp[1] > 0
8676 && (player[i].ent->oldmp >= (player[i].ent->modeldata.mp * .33)
8677 && player[i].ent->oldmp < (player[i].ent->modeldata.mp * .66))) {
8678 drawmethod.table = player[i].ent->colourmap;
8679 spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0],
8680 savedata.windowpos + mpicon[i][1], 10000,
8681 player[i].ent->modeldata.iconmp[1], &drawmethod, 0);
8682 } else if(player[i].ent->modeldata.iconmp[2] > 0
8683 && (player[i].ent->oldmp >= 0
8684 && player[i].ent->oldmp < (player[i].ent->modeldata.mp * .33))) {
8685 drawmethod.table = player[i].ent->colourmap;
8686 spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0],
8687 savedata.windowpos + mpicon[i][1], 10000,
8688 player[i].ent->modeldata.iconmp[2], &drawmethod, 0);
8689 } else if(player[i].ent->modeldata.iconmp[0] > 0
8690 && player[i].ent->modeldata.iconmp[1] == -1
8691 && player[i].ent->modeldata.iconmp[2] == -1) {
8692 drawmethod.table = player[i].ent->colourmap;
8693 spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0],
8694 savedata.windowpos + mpicon[i][1], 10000,
8695 player[i].ent->modeldata.iconmp[0], &drawmethod, 0);
8699 font_printf(videomodes.shiftpos[i] + plifeX[i][0], savedata.windowpos + plifeX[i][1],
8700 plifeX[i][2], 0, "x");
8701 font_printf(videomodes.shiftpos[i] + plifeN[i][0], savedata.windowpos + plifeN[i][1],
8702 plifeN[i][2], 0, "%i", player[i].lives);
8704 if(rush[0] && player[i].ent->rush[0] > 1 && borTime <= player[i].ent->rushtime) {
8705 font_printf(videomodes.shiftpos[i] + prush[i][0], prush[i][1], rush[2], 0, "%s",
8706 rush_names[0]);
8707 font_printf(videomodes.shiftpos[i] + prush[i][2], prush[i][3], rush[3], 0, "%i",
8708 player[i].ent->rush[0]);
8710 if(rush[0] != 2) {
8711 font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s",
8712 rush_names[1]);
8713 font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i",
8714 player[i].ent->rush[1]);
8718 if(rush[0] == 2) {
8719 font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s",
8720 rush_names[1]);
8721 font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i",
8722 player[i].ent->rush[1]);
8725 if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife) { // Displays life unless overridden by flag
8726 font_printf(videomodes.shiftpos[i] + ename[i][0], savedata.windowpos + ename[i][1],
8727 ename[i][2], 0, player[i].ent->opponent->name);
8728 if(player[i].ent->opponent->health <= 0)
8729 icon = player[i].ent->opponent->modeldata.icondie;
8730 else if(player[i].ent->opponent->inpain)
8731 icon = player[i].ent->opponent->modeldata.iconpain;
8732 else if(player[i].ent->opponent->getting)
8733 icon = player[i].ent->opponent->modeldata.iconget;
8734 else
8735 icon = player[i].ent->opponent->modeldata.icon;
8737 if(icon >= 0) {
8738 drawmethod.table = player[i].ent->opponent->colourmap;
8739 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
8742 } else if(player[i].joining && player[i].name[0]) {
8743 model = findmodel(player[i].name);
8744 font_printf(videomodes.shiftpos[i] + pnameJ[i][0], savedata.windowpos + pnameJ[i][1],
8745 pnameJ[i][6], 0, player[i].name);
8746 if(nojoin)
8747 font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3],
8748 pnameJ[i][6], 0, "Please Wait");
8749 else
8750 font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3],
8751 pnameJ[i][6], 0, "Select Hero");
8752 icon = model->icon;
8754 if(icon >= 0 && !player[i].colourmap) {
8755 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], picon[i][1], 10000, icon, NULL,
8757 } else if(icon >= 0) {
8758 drawmethod.table = model->colourmap[player[i].colourmap - 1];
8759 spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], picon[i][1], 10000, icon,
8760 &drawmethod, 0);
8764 if((player[i].playkeys & FLAG_ANYBUTTON || (skipselect && (*skipselect)[current_set][i])) && !freezeall && !nojoin) // Can't join while animations are frozen
8766 // reports error if players try to use the same character and sameplay mode is off
8767 if(sameplayer) {
8768 for(x = 0; x < maxplayers[current_set]; x++) {
8769 if((i != x) && (!strcmp(player[i].name, player[x].name))) {
8770 tperror = i + 1;
8771 break;
8776 if(!tperror) { // Fixed so players can be selected if other player is no longer va //4player player[i].playkeys = player[i].newkeys = 0;
8777 player[i].lives = PLAYER_LIVES; // to address new lives settings
8778 player[i].joining = 0;
8779 player[i].hasplayed = 1;
8780 player[i].spawnhealth = model->health;
8781 player[i].spawnmp = model->mp;
8782 spawnplayer(i);
8783 execute_join_script(i);
8785 player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0;
8787 if(!nodropen)
8788 drop_all_enemies(); //27-12-2004 If drop enemies is on, drop all enemies
8790 if(!level->noreset)
8791 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
8794 } else if(player[i].playkeys & FLAG_MOVELEFT) {
8795 player[i].colourmap = i;
8796 model = prevplayermodel(model);
8797 strcpy(player[i].name, model->name);
8799 while( // Keep looping until a non-hmap is found
8800 ((model->hmap1) && (model->hmap2) &&
8801 player[i].colourmap >= model->hmap1 &&
8802 player[i].colourmap <= model->hmap2)
8804 player[i].colourmap++;
8805 if(player[i].colourmap > model->maps_loaded)
8806 player[i].colourmap = 0;
8809 player[i].playkeys = 0;
8810 } else if(player[i].playkeys & FLAG_MOVERIGHT) {
8811 player[i].colourmap = i;
8812 model = nextplayermodel(model);
8813 strcpy(player[i].name, model->name);
8815 while( // Keep looping until a non-hmap is found
8816 ((model->hmap1) && (model->hmap2) &&
8817 player[i].colourmap >= model->hmap1 &&
8818 player[i].colourmap <= model->hmap2)
8820 player[i].colourmap++;
8821 if(player[i].colourmap > model->maps_loaded)
8822 player[i].colourmap = 0;
8825 player[i].playkeys = 0;
8828 // don't like a characters color try a new one!
8829 else if(player[i].playkeys & FLAG_MOVEUP && colourselect) {
8831 do {
8832 player[i].colourmap++;
8834 if(player[i].colourmap > model->maps_loaded)
8835 player[i].colourmap = 0;
8837 while( // Keep looping until a non-fmap is found
8838 (model->fmap &&
8839 player[i].colourmap - 1 == model->fmap - 1) ||
8840 ((model->hmap1) && (model->hmap2) &&
8841 player[i].colourmap - 1 >= model->hmap1 - 1 &&
8842 player[i].colourmap - 1 <= model->hmap2 - 1)
8845 player[i].playkeys = 0;
8846 } else if(player[i].playkeys & FLAG_MOVEDOWN && colourselect) {
8848 do {
8849 player[i].colourmap--;
8851 if(player[i].colourmap < 0)
8852 player[i].colourmap = model->maps_loaded;
8854 while( // Keep looping until a non-fmap is found
8855 (model->fmap &&
8856 player[i].colourmap - 1 == model->fmap - 1) ||
8857 ((model->hmap1) && (model->hmap2) &&
8858 player[i].colourmap - 1 >= model->hmap1 - 1 &&
8859 player[i].colourmap - 1 <= model->hmap2 - 1)
8862 player[i].playkeys = 0;
8864 } else if(player[i].credits || credits || (!player[i].hasplayed && noshare)) {
8865 if(player[i].credits && (borTime / (GAME_SPEED * 2)) & 1)
8866 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5],
8867 pnameJ[i][6], 0, "Credit %i", player[i].credits);
8868 else if(credits && (borTime / (GAME_SPEED * 2)) & 1)
8869 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5],
8870 pnameJ[i][6], 0, "Credit %i", credits);
8871 else if(nojoin)
8872 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5],
8873 pnameJ[i][6], 0, "Please Wait");
8874 else
8875 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5],
8876 pnameJ[i][6], 0, "Press Start");
8878 if(player[i].playkeys & FLAG_START) {
8879 player[i].lives = 0;
8880 model = (skipselect
8881 && (*skipselect)[current_set][i]) ? findmodel((*skipselect)[current_set][i]) :
8882 nextplayermodel(NULL);
8883 strncpy(player[i].name, model->name, MAX_NAME_LEN);
8884 player[i].colourmap = i;
8885 // Keep looping until a non-hmap is found
8886 while(model->hmap1 && model->hmap2 && player[i].colourmap >= model->hmap1
8887 && player[i].colourmap <= model->hmap2) {
8888 player[i].colourmap++;
8889 if(player[i].colourmap > model->maps_loaded)
8890 player[i].colourmap = 0;
8893 player[i].joining = 1;
8894 player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0;
8896 if(!level->noreset)
8897 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
8899 if(!player[i].hasplayed && noshare)
8900 player[i].credits = CONTINUES;
8902 if(!creditscheat) {
8903 if(noshare)
8904 --player[i].credits;
8905 else
8906 --credits;
8907 if(continuescore[current_set] == 1)
8908 player[i].score = 0;
8909 if(continuescore[current_set] == 2)
8910 player[i].score = player[i].score + 1;
8913 } else {
8914 font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5],
8915 pnameJ[i][6], 0, "GAME OVER");
8917 } // end of for
8919 if(savedata.debuginfo) {
8920 spriteq_add_box(0, videomodes.dOffset - 12, videomodes.hRes, videomodes.dOffset + 12, 0x0FFFFFFE, 0, 0);
8921 font_printf(2, videomodes.dOffset - 10, 0, 0, "FPS: %02d", getFPS());
8924 dt = timeleft / COUNTER_SPEED;
8925 if(dt >= 99) {
8926 dt = 99;
8928 oldtime = 99;
8930 if(dt <= 0) {
8931 dt = 0;
8932 oldtime = 99;
8935 if(dt < oldtime || oldtime == 0) {
8936 execute_timetick_script(dt, go_time);
8937 oldtime = dt;
8940 if(timeicon >= 0)
8941 spriteq_add_sprite(videomodes.hShift + timeicon_offsets[0], savedata.windowpos + timeicon_offsets[1],
8942 10000, timeicon, NULL, 0);
8943 if(!level->notime)
8944 font_printf(videomodes.hShift + timeloc[0] + 2, savedata.windowpos + timeloc[1] + 2, timeloc[5], 0,
8945 "%02i", dt);
8946 if(showtimeover)
8947 font_printf(videomodes.hShift + 113, videomodes.vShift + savedata.windowpos + 110, timeloc[5], 0,
8948 "TIME OVER");
8950 if(dt < 99)
8951 showtimeover = 0;
8953 if(go_time > borTime) {
8954 dt = (go_time - borTime) % GAME_SPEED;
8956 if(dt < GAME_SPEED / 2) {
8957 if(level->scrolldir & SCROLL_LEFT) { //TODO: upward and downward go
8959 if(golsprite >= 0)
8960 spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, golsprite, NULL, 0); // new sprite for left direction
8961 else {
8962 drawmethod.table = 0;
8963 drawmethod.flipx = 1;
8964 spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, gosprite, &drawmethod, 0);
8967 if(gosound == 0) {
8969 sound_play_sample(samples.go, 0, savedata.effectvol, savedata.effectvol, 100); // 26-12-2004 Play go sample as arrow flashes
8971 gosound = 1; // 26-12-2004 Sets sample as already played - stops sample repeating too much
8973 } else if(level->scrolldir & SCROLL_RIGHT) {
8974 spriteq_add_sprite(videomodes.hRes - 40, 60 + videomodes.vShift, 10000, gosprite, NULL,
8977 if(gosound == 0) {
8979 sound_play_sample(samples.go, 0, savedata.effectvol, savedata.effectvol, 100); // 26-12-2004 Play go sample as arrow flashes
8981 gosound = 1; // 26-12-2004 Sets sample as already played - stops sample repeating too much
8984 } else
8985 gosound = 0; //26-12-2004 Resets go sample after loop so it can be played again next time
8990 // draw boss status on screen
8991 void drawenemystatus(entity * ent) {
8992 s_drawmethod drawmethod;
8993 int icon;
8995 if(ent->modeldata.namex > -1000 && ent->modeldata.namey > -1000)
8996 font_printf(ent->modeldata.namex, ent->modeldata.namey, 0, 0, "%s", ent->name);
8998 if(ent->modeldata.iconx > -1000 && ent->modeldata.icony > -1000) {
8999 if(ent->health <= 0)
9000 icon = ent->modeldata.icondie;
9001 else if(ent->inpain)
9002 icon = ent->modeldata.iconpain;
9003 else if(ent->getting)
9004 icon = ent->modeldata.iconget;
9005 else
9006 icon = ent->modeldata.icon;
9008 if(icon >= 0) {
9009 drawmethod = plainmethod;
9010 drawmethod.table = ent->colourmap;
9011 spriteq_add_sprite(ent->modeldata.iconx, ent->modeldata.icony, HUD_Z, icon, &drawmethod, 0);
9015 if(ent->modeldata.health && ent->modeldata.hpx > -1000 && ent->modeldata.hpy > -1000)
9016 bar(ent->modeldata.hpx, ent->modeldata.hpy, ent->oldhealth, ent->modeldata.health,
9017 &(ent->modeldata.hpbarstatus));
9021 void drawstatus() {
9022 int i;
9024 for(i = 0; i < MAX_PLAYERS; i++) {
9025 if(player[i].ent) {
9026 // Health bars
9027 bar(videomodes.shiftpos[i] + plife[i][0], savedata.windowpos + plife[i][1],
9028 player[i].ent->oldhealth, player[i].ent->modeldata.health, &lbarstatus);
9029 if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife
9030 && player[i].ent->opponent->modeldata.health)
9031 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
9032 // Draw mpbar
9033 if(player[i].ent->modeldata.mp) {
9034 bar(videomodes.shiftpos[i] + pmp[i][0], savedata.windowpos + pmp[i][1],
9035 player[i].ent->oldmp, player[i].ent->modeldata.mp, &mpbarstatus);
9040 // Time box
9041 if(!level->notime && !timeloc[4]) // Only draw if notime is set to 0 or not specified
9043 spriteq_add_line(videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1],
9044 videomodes.hShift + timeloc[0] + timeloc[2], savedata.windowpos + timeloc[1], HUD_Z,
9045 colors.black, 0);
9046 spriteq_add_line(videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1],
9047 videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1] + timeloc[3], HUD_Z,
9048 colors.black, 0);
9049 spriteq_add_line(videomodes.hShift + timeloc[0] + timeloc[2], savedata.windowpos + timeloc[1],
9050 videomodes.hShift + timeloc[0] + timeloc[2],
9051 savedata.windowpos + timeloc[1] + timeloc[3], HUD_Z, colors.black, 0);
9052 spriteq_add_line(videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1] + timeloc[3],
9053 videomodes.hShift + timeloc[0] + timeloc[2],
9054 savedata.windowpos + timeloc[1] + timeloc[3], HUD_Z, colors.black, 0);
9055 spriteq_add_line(videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] - 1,
9056 videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] - 1,
9057 HUD_Z + 1, colors.white, 0);
9058 spriteq_add_line(videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] - 1,
9059 videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1,
9060 HUD_Z + 1, colors.white, 0);
9061 spriteq_add_line(videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] - 1,
9062 videomodes.hShift + timeloc[0] + timeloc[2] - 1,
9063 savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1, colors.white, 0);
9064 spriteq_add_line(videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1,
9065 videomodes.hShift + timeloc[0] + timeloc[2] - 1,
9066 savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1, colors.white, 0);
9070 void update_loading(s_loadingbar * s, int value, int max) {
9071 static unsigned int lasttick = 0;
9072 static unsigned int soundtick = 0;
9073 static unsigned int keybtick = 0;
9074 int pos_x = s->bx + videomodes.hShift;
9075 int pos_y = s->by + videomodes.vShift;
9076 int size_x = s->bsize;
9077 int text_x = s->tx + videomodes.hShift;
9078 int text_y = s->ty + videomodes.vShift;
9079 unsigned int ticks = timer_gettick();
9081 if(ticks - soundtick > 20) {
9082 sound_update_music();
9083 soundtick = ticks;
9086 if(ticks - keybtick > 250) {
9087 control_update(playercontrolpointers, 1); // Respond to exit and/or fullscreen requests from user/OS
9088 if(quit_game)
9089 leave_game();
9090 keybtick = ticks;
9093 if(ticks - lasttick > s->refreshMs || value < 0 || value == max) { // Negative value forces a repaint. used when only bg is drawn for the first time
9094 if(s->set) {
9095 if(value < 0)
9096 value = 0;
9097 if(isLoadingScreenTypeBar(s->set)) {
9098 loadingbarstatus.sizex = size_x;
9099 bar(pos_x, pos_y, value, max, &loadingbarstatus);
9101 font_printf(text_x, text_y, s->tf, 0, "Loading...");
9102 if(isLoadingScreenTypeBg(s->set)) {
9103 if(background)
9104 putscreen(vscreen, background, 0, 0, NULL);
9105 else
9106 clearscreen(vscreen);
9108 spriteq_draw(vscreen, 0);
9109 video_copy_screen(vscreen);
9110 spriteq_clear();
9111 } else if(value < 0) { // Original BOR v1.0029 used this method. Since loadingbg is optional, we should print this one again.
9112 clearscreen(vscreen);
9113 spriteq_clear();
9114 font_printf(120 + videomodes.hShift, 110 + videomodes.vShift, 0, 0, "Loading...");
9115 spriteq_draw(vscreen, 0);
9116 video_copy_screen(vscreen);
9118 lasttick = ticks;
9122 void addscore(int playerindex, int add) {
9123 unsigned int s = player[playerindex & 3].score;
9124 unsigned int next1up;
9125 ScriptVariant var; // used for execute script
9126 Script *ptempscript = pcurrentscript;
9128 if(playerindex < 0)
9129 return; //dont score if <0, e.g., npc damage enemy, enemy damage enemy
9131 playerindex &= 3;
9133 next1up = ((s / lifescore) + 1) * lifescore;
9135 s += add;
9136 if(s > 999999999)
9137 s = 999999999;
9139 while(s > next1up) {
9141 sound_play_sample(samples.oneup, 0, savedata.effectvol, savedata.effectvol, 100);
9143 player[playerindex].lives++;
9144 next1up += lifescore;
9147 player[playerindex].score = s;
9149 //execute a script then
9150 if(Script_IsInitialized(game_scripts.score_script + playerindex)) {
9151 ScriptVariant_Clear(&var);
9152 ScriptVariant_ChangeType(&var, VT_INTEGER);
9153 var.lVal = (s32) add;
9154 Script_Set_Local_Variant("score", &var);
9155 Script_Execute(game_scripts.score_script + playerindex);
9156 ScriptVariant_Clear(&var);
9157 Script_Set_Local_Variant("score", &var);
9159 pcurrentscript = ptempscript;
9165 // ---------------------------- Object handling ------------------------------
9167 void freeEntityFactors(entity* e) {
9168 freeAndNull((void**) &e->defense_factors);
9169 freeAndNull((void**) &e->defense_pain);
9170 freeAndNull((void**) &e->defense_knockdown);
9171 freeAndNull((void**) &e->defense_blockpower);
9172 freeAndNull((void**) &e->defense_blockthreshold);
9173 freeAndNull((void**) &e->defense_blockratio);
9174 freeAndNull((void**) &e->defense_blocktype);
9175 freeAndNull((void**) &e->offense_factors);
9178 void free_ent(entity * e) {
9179 int i;
9180 if(!e)
9181 return;
9182 clear_all_scripts(&e->scripts, 2);
9183 free_all_scripts(&e->scripts);
9185 freeEntityFactors(e);
9187 if(e->entvars) {
9188 // Although free_ent will be only called once when the engine is shutting down,
9189 // just clear those in case we forget something
9190 for(i = 0; i < max_entity_vars; i++) {
9191 ScriptVariant_Clear(e->entvars + i);
9193 freeAndNull((void**) &e->entvars);
9195 freeAndNull((void**) &e);
9198 void free_ents() {
9199 int i;
9200 for(i = 0; i < MAX_ENTS; i++)
9201 free_ent(ent_list[i]);
9204 entity *alloc_ent() {
9205 entity *ent = (entity *) calloc(1, sizeof(entity));
9206 if(!ent)
9207 return NULL;
9208 ent->defense_factors = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9209 ent->defense_pain = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9210 ent->defense_knockdown = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9211 ent->defense_blockpower = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9212 ent->defense_blockthreshold = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9213 ent->defense_blockratio = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9214 ent->defense_blocktype = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9215 ent->offense_factors = calloc(sizeof(float), dyn_anim_custom_maxvalues.max_attack_types);
9217 if(max_entity_vars > 0) {
9218 ent->entvars = calloc(sizeof(ScriptVariant), max_entity_vars);
9219 // memset should be OK by now, because VT_EMPTY is zero by value, or else we should use ScriptVariant_Init
9221 alloc_all_scripts(&ent->scripts);
9222 return ent;
9226 int alloc_ents() {
9227 int i;
9228 for(i = 0; i < MAX_ENTS; i++) {
9229 ent_list[i] = alloc_ent();
9230 if(!ent_list[i]) {
9231 free_ents();
9232 return 0;
9234 ent_list[i]->sortid = i * 100;
9236 ent_count = ent_max = 0;
9237 return 1;
9240 // this method initialize an entity's A.I. behaviors
9241 void ent_default_init(entity * e) {
9242 int dodrop;
9243 int wall;
9244 entity *other;
9246 if(!e)
9247 return;
9249 if((!selectScreen && !borTime) || e->modeldata.type != TYPE_PLAYER) {
9250 if(validanim(e, ANI_SPAWN))
9251 ent_set_anim(e, ANI_SPAWN, 0); // use new playerselect spawn anim
9252 //else set_idle(e);
9253 } else if(!selectScreen && borTime && e->modeldata.type == TYPE_PLAYER) // mid-level respawn
9255 if(validanim(e, ANI_RESPAWN))
9256 ent_set_anim(e, ANI_RESPAWN, 0);
9257 else if(validanim(e, ANI_SPAWN))
9258 ent_set_anim(e, ANI_SPAWN, 0);
9259 //else set_idle(e);
9260 } else if(selectScreen && validanim(e, ANI_SELECT))
9261 ent_set_anim(e, ANI_SELECT, 0);
9262 //else set_idle(e);
9264 if(!level) {
9265 if(!e->animation)
9266 set_idle(e);
9267 return;
9270 switch (e->modeldata.type) {
9271 case TYPE_ENDLEVEL:
9272 case TYPE_ITEM:
9273 e->nograb = 1;
9274 break;
9276 case TYPE_PLAYER:
9277 //e->direction = (level->scrolldir != SCROLL_LEFT);
9278 e->takedamage = player_takedamage;
9279 e->think = player_think;
9280 e->trymove = player_trymove;
9282 if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN)) {
9283 e->takeaction = common_spawn;
9284 } else if(!e->animation) {
9285 if(borTime && level->spawn[(int) e->playerindex][2] > e->a) {
9286 e->a = (float) level->spawn[(int) e->playerindex][2];
9287 if(validanim(e, ANI_JUMP))
9288 ent_set_anim(e, ANI_JUMP, 0);
9289 e->takeaction = common_drop;
9292 if(borTime && e->modeldata.makeinv) {
9293 // Spawn invincible code
9294 e->invincible = 1;
9295 e->blink = (e->modeldata.makeinv > 0);
9296 e->invinctime = borTime + ABS(e->modeldata.makeinv);
9297 e->arrowon = 1; // Display the image above the player
9299 break;
9300 case TYPE_NPC: // use NPC(or A.I. player) instread of an enemy subtype or trap subtype, for further A.I. use
9301 if(e->modeldata.multiple == 0)
9302 e->modeldata.multiple = -1;
9304 case TYPE_ENEMY:
9305 e->think = common_think;
9306 if(e->modeldata.subtype == SUBTYPE_BIKER) {
9307 e->nograb = 1;
9308 e->attacking = 1;
9309 //e->direction = (e->x<0);
9310 if(e->modeldata.speed)
9311 e->xdir = (e->direction) ? (e->modeldata.speed) : (-e->modeldata.speed);
9312 else
9313 e->xdir =
9314 (e->direction) ? (1.7 + randf((float) 0.6)) : (-(1.7 + randf((float) 0.6)));
9315 e->takedamage = biker_takedamage;
9316 break;
9318 // define new subtypes
9319 else if(e->modeldata.subtype == SUBTYPE_ARROW) {
9320 e->health = 1;
9321 if(!e->modeldata.speed && !e->modeldata.nomove)
9322 e->modeldata.speed = 2; // Set default speed to 2 for arrows
9323 else if(e->modeldata.nomove)
9324 e->modeldata.speed = 0;
9325 if(e->ptype)
9326 e->base = 0;
9327 else
9328 e->base = e->a;
9329 e->nograb = 1;
9330 e->attacking = 1;
9331 e->takedamage = arrow_takedamage;
9332 break;
9333 } else {
9334 e->trymove = common_trymove;
9335 // Must just be a regular enemy, set defaults accordingly
9336 if(!e->modeldata.speed && !e->modeldata.nomove)
9337 e->modeldata.speed = 1;
9338 else if(e->modeldata.nomove)
9339 e->modeldata.speed = 0;
9340 if(e->modeldata.multiple == 0)
9341 e->modeldata.multiple = 5;
9342 e->takedamage = common_takedamage; //enemy_takedamage;
9345 if(e->modeldata.subtype == SUBTYPE_NOTGRAB)
9346 e->nograb = 1;
9348 if(validanim(e, ANI_SPAWN) /*|| validanim(e,ANI_RESPAWN) */ ) {
9349 e->takeaction = common_spawn;
9350 } else {
9351 dodrop = (e->modeldata.subtype != SUBTYPE_ARROW && level
9352 && (level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN));
9354 if(dodrop
9355 || (e->x > advancex - 30 && e->x < advancex + videomodes.hRes + 30 && e->a == 0)) {
9356 e->a += videomodes.vRes + randf(40);
9357 e->takeaction = common_drop; //enemy_drop;
9358 if(validanim(e, ANI_JUMP))
9359 ent_set_anim(e, ANI_JUMP, 0);
9362 break;
9363 // define trap type
9364 case TYPE_TRAP:
9365 e->think = trap_think;
9366 e->takedamage = common_takedamage; //enemy_takedamage;
9367 break;
9368 case TYPE_OBSTACLE:
9369 e->nograb = 1;
9370 if(e->health <= 0)
9371 e->dead = 1; // so it won't get hit
9372 e->takedamage = obstacle_takedamage; //obstacle_takedamage;
9373 break;
9374 case TYPE_STEAMER:
9375 e->nograb = 1;
9376 e->think = steamer_think;
9377 e->base = e->a;
9378 break;
9379 case TYPE_TEXTBOX: // New type for displaying text purposes
9380 e->nograb = 1;
9381 e->think = text_think;
9382 break;
9383 case TYPE_SHOT:
9384 e->health = 1;
9385 e->nograb = 1;
9386 e->think = common_think;
9387 e->takedamage = arrow_takedamage;
9388 e->attacking = 1;
9389 if(!e->model->speed && !e->modeldata.nomove)
9390 e->modeldata.speed = 2; // Set default speed to 2 for arrows
9391 else if(e->modeldata.nomove)
9392 e->modeldata.speed = 0;
9393 if(e->ptype)
9394 e->base = 0;
9395 else
9396 e->base = e->a;
9397 break;
9398 case TYPE_NONE:
9399 e->nograb = 1;
9400 if(e->modeldata.subject_to_gravity < 0)
9401 e->modeldata.subject_to_gravity = 1;
9402 //e->base=e->a; //complained?
9403 if(e->modeldata.no_adjust_base < 0)
9404 e->modeldata.no_adjust_base = 1;
9406 if(validanim(e, ANI_WALK)) {
9407 if(e->direction)
9408 e->xdir = e->modeldata.speed;
9409 else
9410 e->xdir = -(e->modeldata.speed);
9411 e->think = anything_walk;
9413 common_walk_anim(e);
9414 //ent_set_anim(e, ANI_WALK, 0);
9416 break;
9417 case TYPE_PANEL:
9418 e->nograb = 1;
9419 break;
9421 if(!e->animation) {
9422 set_idle(e);
9425 if(e->modeldata.multiple < 0)
9426 e->modeldata.multiple = 0;
9428 if(e->modeldata.subject_to_platform > 0 && (other = check_platform_below(e->x, e->z, e->a + 1)) && other != e)
9429 e->base += other->a + other->animation->platform[other->animpos][7];
9430 else if(e->modeldata.subject_to_wall > 0 && (wall = checkwall_below(e->x, e->z, 9999999)) >= 0)
9431 e->base += level->walls[wall].alt;
9434 void ent_spawn_ent(entity * ent) {
9435 entity *s_ent = NULL;
9436 float *spawnframe = ent->animation->spawnframe;
9437 // spawn point relative to current entity
9438 if(spawnframe[4] == 0)
9439 s_ent =
9440 spawn(ent->x + ((ent->direction) ? spawnframe[1] : -spawnframe[1]), ent->z + spawnframe[2],
9441 ent->a + spawnframe[3], ent->direction, NULL, ent->animation->subentity, NULL);
9442 //relative to screen position
9443 else if(spawnframe[4] == 1) {
9444 if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN))
9445 s_ent =
9446 spawn(advancex + spawnframe[1], advancey + spawnframe[2], spawnframe[3], 0, NULL,
9447 ent->animation->subentity, NULL);
9448 else
9449 s_ent =
9450 spawn(advancex + spawnframe[1], spawnframe[2], spawnframe[3], 0, NULL,
9451 ent->animation->subentity, NULL);
9453 //absolute position in level
9454 else
9455 s_ent =
9456 spawn(spawnframe[1], spawnframe[2], spawnframe[3] + 0.001, 0, NULL, ent->animation->subentity,
9457 NULL);
9459 if(s_ent) {
9460 //ent_default_init(s_ent);
9461 if(s_ent->modeldata.type & TYPE_SHOT)
9462 s_ent->playerindex = ent->playerindex;
9463 if(s_ent->modeldata.subtype == SUBTYPE_ARROW)
9464 s_ent->owner = ent;
9465 s_ent->parent = ent; //maybe used by A.I.
9466 execute_onspawn_script(s_ent);
9470 void ent_summon_ent(entity * ent) {
9471 entity *s_ent = NULL;
9472 float *spawnframe = ent->animation->summonframe;
9473 // spawn point relative to current entity
9474 if(spawnframe[4] == 0)
9475 s_ent =
9476 spawn(ent->x + ((ent->direction) ? spawnframe[1] : -spawnframe[1]), ent->z + spawnframe[2],
9477 ent->a + spawnframe[3], ent->direction, NULL, ent->animation->subentity, NULL);
9478 //relative to screen position
9479 else if(spawnframe[4] == 1) {
9480 if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN))
9481 s_ent =
9482 spawn(advancex + spawnframe[1], advancey + spawnframe[2], spawnframe[3], 0, NULL,
9483 ent->animation->subentity, NULL);
9484 else
9485 s_ent =
9486 spawn(advancex + spawnframe[1], spawnframe[2], spawnframe[3], 0, NULL,
9487 ent->animation->subentity, NULL);
9489 //absolute position in level
9490 else
9491 s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3], 0, NULL, ent->animation->subentity, NULL);
9493 if(s_ent) {
9494 if(!spawnframe[4])
9495 s_ent->direction = ent->direction;
9496 //ent_default_init(s_ent);
9497 if(s_ent->modeldata.type & TYPE_SHOT)
9498 s_ent->playerindex = ent->playerindex;
9499 if(s_ent->modeldata.subtype == SUBTYPE_ARROW)
9500 s_ent->owner = ent;
9501 //maybe used by A.I.
9502 s_ent->parent = ent;
9503 ent->subentity = s_ent;
9504 execute_onspawn_script(s_ent);
9508 // move here to prevent some duplicated code in ent_sent_anim and update_ents
9509 void update_frame(entity * ent, int f) {
9510 entity *tempself;
9511 entity *dust;
9512 s_attack attack;
9513 float move, movez, movea;
9514 int iDelay, iED_Mode, iED_Capmin, iED_CapMax, iED_RangeMin, iED_RangeMax;
9515 float fED_Factor;
9517 if(f >= ent->animation->numframes) // prevent a crash with invalid frame index.
9518 return;
9520 //important!
9521 tempself = self;
9522 self = ent;
9524 self->animpos = f;
9525 //self->currentsprite = self->animation->sprite[f];
9527 if(self->animating) {
9528 iDelay = self->animation->delay[f];
9529 iED_Mode = self->modeldata.edelay.mode;
9530 fED_Factor = self->modeldata.edelay.factor;
9531 iED_Capmin = self->modeldata.edelay.cap_min;
9532 iED_CapMax = self->modeldata.edelay.cap_max;
9533 iED_RangeMin = self->modeldata.edelay.range_min;
9534 iED_RangeMax = self->modeldata.edelay.range_max;
9536 if(iDelay >= iED_RangeMin && iDelay <= iED_RangeMax) //Regular delay within ignore ranges?
9538 switch (iED_Mode) {
9539 case 1:
9540 iDelay = (int) (iDelay * fED_Factor);
9541 break;
9542 default:
9543 iDelay += (int) fED_Factor;
9544 break;
9547 if(iED_Capmin && iDelay < iED_Capmin) {
9548 iDelay = iED_Capmin;
9550 if(iED_CapMax && iDelay > iED_CapMax) {
9551 iDelay = iED_CapMax;
9555 self->nextanim = borTime + iDelay;
9556 execute_animation_script(self);
9559 if(level && (self->animation->move || self->animation->movez)) {
9560 move = (float) (self->animation->move ? self->animation->move[f] : 0);
9561 movez = (float) (self->animation->movez ? self->animation->movez[f] : 0);
9562 if(self->direction == 0)
9563 move = -move;
9564 if(movez || move) {
9565 if(self->trymove) {
9566 self->trymove(move, movez);
9567 } else {
9568 self->x += move;
9569 self->z += movez;
9574 if(self->animation->seta && self->animation->seta[0] >= 0 && self->base <= 0)
9575 ent->base = (float) ent->animation->seta[0];
9576 else if(!self->animation->seta || self->animation->seta[0] < 0) {
9577 movea = (float) (self->animation->movea ? self->animation->movea[f] : 0);
9578 self->base += movea;
9579 if(movea != 0)
9580 self->altbase += movea;
9581 else
9582 self->altbase = 0;
9585 if(self->animation->flipframe == f)
9586 self->direction = !self->direction;
9588 if(self->animation->weaponframe && self->animation->weaponframe[0] == f) {
9589 dropweapon(2);
9590 set_weapon(self, self->animation->weaponframe[1], 0);
9591 self->idling = 1;
9594 if(self->animation->quakeframe[0] + self->animation->quakeframe[3] == f) {
9595 if(level) {
9596 if(self->animation->quakeframe[3] % 2 || self->animation->quakeframe[2] > 0)
9597 level->quake = self->animation->quakeframe[2];
9598 else
9599 level->quake = self->animation->quakeframe[2] * -1;
9601 if((self->animation->quakeframe[1] - self->animation->quakeframe[3]) > 1)
9602 self->animation->quakeframe[3]++;
9603 else
9604 self->animation->quakeframe[3] = 0;
9606 //spawn / summon /unsummon features
9607 if(self->animation->spawnframe && self->animation->spawnframe[0] == f && self->animation->subentity)
9608 ent_spawn_ent(self);
9610 if(self->animation->summonframe && self->animation->summonframe[0] == f && self->animation->subentity) {
9611 //subentity is dead
9612 if(!self->subentity || self->subentity->dead)
9613 ent_summon_ent(self);
9616 if(self->animation->unsummonframe == f) {
9617 if(self->subentity) {
9618 self = self->subentity;
9619 attack = emptyattack;
9620 attack.dropv[0] = (float) 3;
9621 attack.dropv[1] = (float) 1.2;
9622 attack.dropv[2] = (float) 0;
9623 attack.attack_force = self->health;
9624 attack.attack_type = dyn_anim_custom_maxvalues.max_attack_types;
9625 if(self->takedamage)
9626 self->takedamage(self, &attack);
9627 else
9628 kill(self);
9629 self = ent; // lol ...
9630 self->subentity = NULL;
9634 if(self->animation->soundtoplay)
9635 sound_play_sample(self->animation->soundtoplay[f], 0, savedata.effectvol, savedata.effectvol, 100);
9637 if(self->animation->jumpframe == f) {
9638 // Set custom jumpheight for jumpframes
9639 /*if(self->animation->jumpv > 0) */ toss(self, self->animation->jumpv);
9640 self->xdir = self->direction ? self->animation->jumpx : -self->animation->jumpx;
9641 self->zdir = self->animation->jumpz;
9643 if(self->animation->jumpd >= 0) {
9644 dust = spawn(self->x, self->z, self->a, self->direction, NULL, self->animation->jumpd, NULL);
9645 dust->base = self->a;
9646 dust->autokill = 1;
9647 execute_onspawn_script(dust);
9651 if(self->animation->throwframe == f) {
9652 // For backward compatible thing
9653 // throw stars in the air, hmm, strange
9654 // custstar custknife in animation should be checked first
9655 // then if the entiti is jumping, check star first, if failed, try knife instead
9656 // well, try knife at last, if still failed, try star, or just let if shutdown?
9657 #define __trystar star_spawn(self->x + (self->direction ? 56 : -56), self->z, self->a+67, self->direction)
9658 #define __tryknife knife_spawn(NULL, -1, self->x, self->z, self->a + self->animation->throwa, self->direction, 0, 0)
9659 if(self->animation->custknife >= 0 || self->animation->custpshotno >= 0)
9660 __tryknife;
9661 else if(self->animation->custstar >= 0)
9662 __trystar;
9663 else if(self->jumping) {
9664 if(!__trystar)
9665 __tryknife;
9666 } else if(!__tryknife)
9667 __trystar;
9668 self->reactive = 1;
9671 if(self->animation->shootframe == f) {
9672 knife_spawn(NULL, -1, self->x, self->z, self->a, self->direction, 1, 0);
9673 self->reactive = 1;
9676 if(self->animation->tossframe == f) {
9677 bomb_spawn(NULL, -1, self->x, self->z, self->a + self->animation->throwa, self->direction, 0);
9678 self->reactive = 1;
9680 //important!
9681 self = tempself;
9685 void ent_set_anim(entity * ent, int aninum, int resetable) {
9686 s_anim *ani = NULL;
9688 if(!ent) {
9689 printf("FATAL: tried to set animation with invalid address (no such object)");
9690 return;
9693 if(aninum < 0 || aninum >= dyn_anim_custom_maxvalues.max_animations) {
9694 printf("FATAL: tried to set animation with invalid index (%s, %i)", ent->name, aninum);
9695 return;
9698 if(!validanim(ent, aninum)) {
9699 printf("FATAL: tried to set animation with invalid address (%s, %i)", ent->name, aninum);
9700 return;
9703 ani = ent->modeldata.animation[aninum];
9705 if(!resetable && ent->animation == ani)
9706 return;
9708 if(ani->numframes == 0)
9709 return;
9711 if(aninum != ANI_SLEEP)
9712 ent->sleeptime = borTime + ent->modeldata.sleepwait;
9713 ent->animation = ani;
9714 ent->animnum = aninum; // Stored for nocost usage
9715 ent->animation->animhits = 0;
9717 if(!resetable)
9718 ent->lastanimpos = -1;
9719 ent->animating = 1;
9720 ent->lasthit = ent->grabbing;
9721 ent->altbase = 0;
9723 update_frame(ent, 0);
9728 // 0 = none, 1+ = alternative
9729 void ent_set_colourmap(entity * ent, unsigned int which) {
9730 if(which > MAX_COLOUR_MAPS)
9731 which = 0;
9732 if(which == 0)
9733 ent->colourmap = NULL;
9734 else
9735 ent->colourmap = ent->modeldata.colourmap[which - 1];
9736 ent->map = which;
9739 // used by ent_set_model
9740 void ent_copy_uninit(entity * ent, s_model * oldmodel) {
9741 setDestIfDestNeg_int(&ent->modeldata.multiple, oldmodel->multiple);
9742 setDestIfDestNeg_int((int*)&ent->modeldata.aimove, oldmodel->aimove);
9743 setDestIfDestNeg_int((int*)&ent->modeldata.aiattack, oldmodel->aiattack);
9744 setDestIfDestNeg_char(&ent->modeldata.subject_to_wall, oldmodel->subject_to_wall);
9745 setDestIfDestNeg_char(&ent->modeldata.subject_to_platform, oldmodel->subject_to_platform);
9746 setDestIfDestNeg_char(&ent->modeldata.subject_to_obstacle, oldmodel->subject_to_obstacle);
9747 setDestIfDestNeg_char(&ent->modeldata.subject_to_hole, oldmodel->subject_to_hole);
9748 setDestIfDestNeg_char(&ent->modeldata.subject_to_gravity, oldmodel->subject_to_gravity);
9749 setDestIfDestNeg_char(&ent->modeldata.subject_to_screen, oldmodel->subject_to_screen);
9750 setDestIfDestNeg_char(&ent->modeldata.subject_to_minz, oldmodel->subject_to_minz);
9751 setDestIfDestNeg_char(&ent->modeldata.subject_to_maxz, oldmodel->subject_to_maxz);
9752 setDestIfDestNeg_char(&ent->modeldata.no_adjust_base, oldmodel->no_adjust_base);
9753 setDestIfDestNeg_short(&ent->modeldata.hostile, oldmodel->hostile);
9754 setDestIfDestNeg_short(&ent->modeldata.candamage, oldmodel->candamage);
9755 setDestIfDestNeg_short(&ent->modeldata.projectilehit, oldmodel->projectilehit);
9756 if(!ent->modeldata.health)
9757 ent->modeldata.health = oldmodel->health;
9758 if(!ent->modeldata.mp)
9759 ent->modeldata.mp = oldmodel->mp;
9760 if(ent->modeldata.risetime[0] == -1)
9761 ent->modeldata.risetime[0] = oldmodel->risetime[0];
9763 if(ent->health > ent->modeldata.health)
9764 ent->health = ent->modeldata.health;
9765 if(ent->mp > ent->modeldata.mp)
9766 ent->mp = ent->modeldata.mp;
9770 void ent_set_model(entity * ent, char *modelname) {
9771 s_model *m = NULL;
9772 s_model oldmodel;
9773 if(ent == NULL)
9774 shutdown(1, "FATAL: tried to change model of invalid object");
9775 m = findmodel(modelname);
9776 if(m == NULL)
9777 shutdown(1, "Model not found: '%s'", modelname);
9778 oldmodel = ent->modeldata;
9779 ent->model = m;
9780 ent->modeldata = *m;
9781 ent_copy_uninit(ent, &oldmodel);
9782 ent_set_colourmap(ent, ent->map);
9783 if((!selectScreen && !borTime) || ent->modeldata.type != TYPE_PLAYER) {
9784 // use new playerselect spawn anim
9785 if(validanim(ent, ANI_SPAWN))
9786 ent_set_anim(ent, ANI_SPAWN, 0);
9787 else
9788 ent_set_anim(ent, ANI_IDLE, 0);
9789 } else if(!selectScreen && borTime && ent->modeldata.type == TYPE_PLAYER) {
9790 // mid-level respawn
9791 if(validanim(ent, ANI_RESPAWN))
9792 ent_set_anim(ent, ANI_RESPAWN, 0);
9793 else if(validanim(ent, ANI_SPAWN))
9794 ent_set_anim(ent, ANI_SPAWN, 0);
9795 else
9796 ent_set_anim(ent, ANI_IDLE, 0);
9797 } else if(selectScreen && validanim(ent, ANI_SELECT))
9798 ent_set_anim(ent, ANI_SELECT, 0);
9799 else
9800 ent_set_anim(ent, ANI_IDLE, 0);
9804 entity *spawn(float x, float z, float a, int direction, char *name, int index, s_model * model) {
9805 entity *e = NULL;
9806 int i, id;
9807 float *dfs, *dfsp, *dfsk, *dfsbp, *dfsbt, *dfsbr, *dfsbe, *ofs;
9808 ScriptVariant *vars;
9809 s_scripts scripts_save;
9811 if(!model) {
9812 if(index >= 0)
9813 model = model_cache[index].model;
9814 else if(name)
9815 model = findmodel(name);
9817 // Be a bit more tolerant...
9818 if(model == NULL) {
9819 if(index >= 0)
9820 printf("FATAL: attempt to spawn object with invalid model cache id (%d)!\n", index);
9821 else if(name)
9822 printf("FATAL: attempt to spawn object with invalid model name (%s)!\n", name);
9823 return NULL;
9826 for(i = 0; i < MAX_ENTS; i++) {
9827 if(!ent_list[i]->exists) {
9828 e = ent_list[i];
9829 // save these values, or they will loss when memset called
9830 id = e->sortid;
9831 dfs = e->defense_factors;
9832 dfsp = e->defense_pain;
9833 dfsk = e->defense_knockdown;
9834 dfsbp = e->defense_blockpower;
9835 dfsbt = e->defense_blockthreshold;
9836 dfsbr = e->defense_blockratio;
9837 dfsbe = e->defense_blocktype;
9838 ofs = e->offense_factors;
9839 vars = e->entvars;
9840 memset(dfs, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9841 memset(dfsp, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9842 memset(dfsk, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9843 memset(dfsbp, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9844 memset(dfsbt, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9845 memset(dfsbr, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9846 memset(dfsbe, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9847 memset(ofs, 0, sizeof(float) * dyn_anim_custom_maxvalues.max_attack_types);
9848 // clear up
9849 clear_all_scripts(&e->scripts, 1);
9851 scripts_save = e->scripts;
9853 memset(e, 0, sizeof(entity));
9855 // add to list and count current entities
9856 e->exists = 1;
9857 ent_count++;
9859 e->modeldata = *model; // copy the entir model data here
9860 e->model = model;
9861 e->defaultmodel = model;
9863 e->scripts = scripts_save;
9865 // copy from model a fresh script
9867 copy_all_scripts(&model->scripts, &e->scripts, 1);
9869 if(ent_count > ent_max)
9870 ent_max = ent_count;
9871 e->timestamp = borTime; // log time so update function will ignore it if it is new
9873 e->health = e->modeldata.health;
9874 e->mp = e->modeldata.mp;
9875 e->knockdowncount = e->modeldata.knockdowncount;
9876 e->x = x;
9877 e->z = z;
9878 e->a = a;
9879 e->direction = direction;
9880 e->nextthink = borTime + 1;
9881 e->lifespancountdown = model->lifespan; // new life span countdown
9882 if((e->modeldata.type & (TYPE_PLAYER | TYPE_SHOT)) && level && (level->nohit || savedata.mode))
9883 e->modeldata.hostile &= ~TYPE_PLAYER;
9884 if(e->modeldata.type == TYPE_PLAYER)
9885 e->playerindex = currentspawnplayer;
9887 if(e->modeldata.type == TYPE_TEXTBOX)
9888 textbox = e;
9890 strncpy(e->name, e->modeldata.name, MAX_NAME_LEN);
9891 // copy back the value
9892 e->sortid = id;
9893 e->defense_factors = dfs;
9894 e->defense_pain = dfsp;
9895 e->defense_knockdown = dfsk;
9896 e->defense_blockpower = dfsbp;
9897 e->defense_blockthreshold = dfsbt;
9898 e->defense_blockratio = dfsbr;
9899 e->defense_blocktype = dfsbe;
9900 e->offense_factors = ofs;
9901 e->entvars = vars;
9903 ent_default_init(e);
9904 return e;
9907 return NULL;
9912 // Break the link an entity has with another one
9913 void ent_unlink(entity * e) {
9914 if(e->link) {
9915 e->link->link = NULL;
9916 e->link->grabbing = NULL;
9918 e->link = NULL;
9919 e->grabbing = NULL;
9924 // Link two entities together
9925 void ents_link(entity * e1, entity * e2) {
9926 ent_unlink(e1);
9927 ent_unlink(e2);
9928 e1->grabbing = e2; // Added for platform layering
9929 e1->link = e2;
9930 e2->link = e1;
9935 void kill(entity * victim) {
9936 int i = 0;
9937 s_attack attack;
9938 entity *tempent = self;
9940 execute_onkill_script(victim);
9942 if(!victim || !victim->exists)
9943 return;
9945 if(victim->modeldata.type == TYPE_SHOT && player[(int) victim->playerindex].ent)
9946 player[(int) victim->playerindex].ent->cantfire = 0;
9948 ent_unlink(victim);
9949 victim->weapent = NULL;
9950 victim->health = 0;
9951 victim->exists = 0;
9952 ent_count--;
9954 clear_all_scripts(&victim->scripts, 1);
9956 if(victim->parent && victim->parent->subentity == victim)
9957 victim->parent->subentity = NULL;
9958 victim->parent = NULL;
9959 if(victim->modeldata.summonkill) {
9960 attack = emptyattack;
9961 attack.attack_type = dyn_anim_custom_maxvalues.max_attack_types;
9962 attack.dropv[0] = (float) 3;
9963 attack.dropv[1] = (float) 1.2;
9964 attack.dropv[2] = (float) 0;
9966 // kill minions
9967 if(victim->modeldata.summonkill == 1 && victim->subentity) {
9968 // kill only summoned one
9969 victim->subentity->parent = NULL;
9970 self = victim->subentity;
9971 attack.attack_force = self->health;
9972 if(self->takedamage && !level_completed)
9973 self->takedamage(self, &attack);
9974 else
9975 kill(self);
9977 victim->subentity = NULL;
9979 if(victim == player[0].ent)
9980 player[0].ent = NULL;
9981 else if(victim == player[1].ent)
9982 player[1].ent = NULL;
9983 else if(victim == player[2].ent)
9984 player[2].ent = NULL;
9985 else if(victim == player[3].ent)
9986 player[3].ent = NULL;
9988 if(victim == smartbomber)
9989 smartbomber = NULL;
9990 if(victim == textbox)
9991 textbox = NULL;
9993 for(i = 0; i < ent_max; i++) {
9994 if(ent_list[i]->exists) {
9995 // kill all minions
9996 self = ent_list[i];
9997 if(self->parent == victim) {
9998 self->parent = NULL;
9999 if(victim->modeldata.summonkill == 2) {
10000 attack.attack_force = self->health;
10001 if(self->takedamage && !level_completed)
10002 self->takedamage(self, &attack);
10003 else
10004 kill(self);
10007 if(self->owner == victim) {
10008 self->owner = victim->owner;
10010 if(self->opponent == victim)
10011 self->opponent = NULL;
10012 if(self->bound == victim)
10013 self->bound = NULL;
10014 if(self->landed_on_platform == victim)
10015 self->landed_on_platform = NULL;
10016 if(self->hithead == victim)
10017 self->hithead = NULL;
10018 if(!textbox && self->modeldata.type == TYPE_TEXTBOX)
10019 textbox = self;
10022 self = tempent;
10026 void kill_all() {
10027 int i;
10028 entity *e = NULL;
10029 for(i = 0; i < ent_max; i++) {
10030 e = ent_list[i];
10031 if(e && e->exists)
10032 execute_onkill_script(e);
10033 e->exists = 0; // well, no need to use kill function
10035 textbox = smartbomber = NULL;
10036 ent_max = ent_count = 0;
10037 borTime = 0;
10041 int checkhit(entity * attacker, entity * target, int counter) {
10042 short *coords1;
10043 short *coords2;
10044 int x1, x2, y1, y2;
10045 float medx, medy;
10046 int debug_coords[2][4];
10047 int topleast, bottomleast, leftleast, rightleast;
10048 float zdist = 0;
10050 if(attacker == target || !target->animation->bbox_coords ||
10051 !attacker->animation->attacks || !target->animation->vulnerable[target->animpos] ||
10052 ((attacker->modeldata.type == TYPE_PLAYER && target->modeldata.type == TYPE_PLAYER) && savedata.mode))
10053 return 0;
10056 coords1 = attacker->animation->attacks[attacker->animpos]->attack_coords;
10058 if(!counter)
10059 coords2 = target->animation->bbox_coords[target->animpos];
10060 else if((target->animation->attacks && target->animation->attacks[target->animpos])
10061 && target->animation->attacks[target->animpos]->counterattack <=
10062 attacker->animation->attacks[attacker->animpos]->counterattack)
10063 coords2 = target->animation->attacks[target->animpos]->attack_coords;
10064 else
10065 return 0;
10067 if(coords1[4])
10068 zdist += coords1[4];
10069 else
10070 zdist += attacker->modeldata.grabdistance / 3;
10071 if(coords2[4])
10072 zdist += coords2[4];
10074 if(diff(attacker->z, target->z) > zdist)
10075 return 0;
10077 x1 = (int) (attacker->x);
10078 y1 = (int) (attacker->z - attacker->a);
10079 x2 = (int) (target->x);
10080 y2 = (int) (target->z - target->a);
10083 if(attacker->direction == 0) {
10084 debug_coords[0][0] = x1 - coords1[2];
10085 debug_coords[0][1] = y1 + coords1[1];
10086 debug_coords[0][2] = x1 - coords1[0];
10087 debug_coords[0][3] = y1 + coords1[3];
10088 } else {
10089 debug_coords[0][0] = x1 + coords1[0];
10090 debug_coords[0][1] = y1 + coords1[1];
10091 debug_coords[0][2] = x1 + coords1[2];
10092 debug_coords[0][3] = y1 + coords1[3];
10094 if(target->direction == 0) {
10095 debug_coords[1][0] = x2 - coords2[2];
10096 debug_coords[1][1] = y2 + coords2[1];
10097 debug_coords[1][2] = x2 - coords2[0];
10098 debug_coords[1][3] = y2 + coords2[3];
10099 } else {
10100 debug_coords[1][0] = x2 + coords2[0];
10101 debug_coords[1][1] = y2 + coords2[1];
10102 debug_coords[1][2] = x2 + coords2[2];
10103 debug_coords[1][3] = y2 + coords2[3];
10106 if(debug_coords[0][0] > debug_coords[1][2])
10107 return 0;
10108 if(debug_coords[1][0] > debug_coords[0][2])
10109 return 0;
10110 if(debug_coords[0][1] > debug_coords[1][3])
10111 return 0;
10112 if(debug_coords[1][1] > debug_coords[0][3])
10113 return 0;
10115 // Find center of attack area
10116 leftleast = MAX(debug_coords[0][0], debug_coords[1][0]);
10117 topleast = MAX(debug_coords[0][1], debug_coords[1][1]);
10118 rightleast = MIN(debug_coords[0][2], debug_coords[1][2]);
10119 bottomleast = MIN(debug_coords[0][3], debug_coords[1][3]);
10121 medx = (float) (leftleast + rightleast) / 2;
10122 medy = (float) (topleast + bottomleast) / 2;
10124 // Now convert these coords to 3D
10125 lasthitx = medx;
10127 if(attacker->z > target->z)
10128 lasthitz = attacker->z + 1; // Changed so flashes always spawn in front
10129 else
10130 lasthitz = target->z + 1;
10132 lasthita = lasthitz - medy;
10133 lasthitt = attacker->animation->attacks[attacker->animpos]->attack_type;
10134 lasthitc = 1;
10135 return 1;
10139 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
10140 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
10141 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
10142 within the bottom/top and the left/right area.
10144 static int testhole_or_wall(s_hole* area, float x, float z) {
10145 float coef1, coef2;
10146 if(z < area->z && z > area->z - area->depth) {
10147 coef1 = (area->z - z) * ((area->upperleft - area->lowerleft) / area->depth);
10148 coef2 = (area->z - z) * ((area->upperright - area->lowerright) / area->depth);
10149 if(x > area->x + area->lowerleft + coef1
10150 && x < area->x + area->lowerright + coef2)
10151 return 1;
10153 return 0;
10155 int testhole(int hole, float x, float z) {
10156 return testhole_or_wall(&level->holes[hole], x, z);
10159 int testwall(int wall, float x, float z) {
10160 return testhole_or_wall((s_hole*) (&level->walls[wall]), x, z);
10163 /// find all holes here and return the count
10164 int checkholes(float x, float z) {
10165 int i, c;
10167 for(i = 0, c = 0; i < level->numholes; i++)
10168 c += (level->holesfound[i] = testhole(i, x, z));
10170 return c;
10173 // find the 1st hole here
10174 int checkhole(float x, float z) {
10175 int i;
10177 if(level == NULL)
10178 return 0;
10180 if(level->exit_hole) {
10181 if(x > level->width - (PLAYER_MAX_Z - z))
10182 return 2;
10185 for(i = 0; i < level->numholes; i++) {
10186 if(testhole(i, x, z)) {
10187 holez = i;
10188 return 1;
10191 return 0;
10196 // find all walls here within altitude1 and 2, return the count
10197 int checkwalls(float x, float z, float a1, float a2) {
10198 int i, c;
10200 for(i = 0, c = 0; i < level->numwalls; i++)
10201 c += (level->wallsfound[i] =
10202 (testwall(i, x, z) && level->walls[i].alt >= a1 && level->walls[i].alt <= a2));
10204 return c;
10207 // get a highest wall below this altitude
10208 int checkwall_below(float x, float z, float a) {
10209 float maxa;
10210 int i, ind;
10212 if(level == NULL)
10213 return -1;
10215 maxa = 0;
10216 ind = -1;
10217 for(i = 0; i < level->numwalls; i++) {
10218 if(testwall(i, x, z) && level->walls[i].alt < a + 0.1 && level->walls[i].alt > maxa) {
10219 maxa = level->walls[i].alt;
10220 ind = i;
10224 return ind;
10227 // return the 1st wall found here
10228 int checkwall(float x, float z) {
10229 int i;
10230 if(level == NULL)
10231 return -1;
10233 for(i = 0; i < level->numwalls; i++)
10234 if(testwall(i, x, z))
10235 return i;
10237 return -1;
10241 Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
10242 the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
10243 left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
10244 within the bottom/top and the left/right area.
10246 int testplatform(entity * plat, float x, float z) {
10247 float coef1, coef2;
10248 float offz, offx;
10249 if(!plat->animation || !plat->animation->platform || !plat->animation->platform[plat->animpos][7])
10250 return 0;
10251 offz = plat->z + plat->animation->platform[plat->animpos][1];
10252 offx = plat->x + plat->animation->platform[plat->animpos][0];
10253 if(z <= offz && z > offz - plat->animation->platform[plat->animpos][6]) {
10254 coef1 = (offz - z) * ((plat->animation->platform[plat->animpos][2] -
10255 plat->animation->platform[plat->animpos][3]) /
10256 plat->animation->platform[plat->animpos][6]);
10257 coef2 =
10258 (offz -
10259 z) * ((plat->animation->platform[plat->animpos][4] -
10260 plat->animation->platform[plat->animpos][5]) / plat->animation->platform[plat->animpos][6]);
10262 if(x > offx + plat->animation->platform[plat->animpos][3] + coef1 &&
10263 x < offx + plat->animation->platform[plat->animpos][5] + coef2)
10264 return 1;
10266 return 0;
10270 //find the first platform between these 2 altitudes
10271 entity *check_platform_between(float x, float z, float amin, float amax) {
10272 entity *plat = NULL;
10273 int i;
10275 if(level == NULL)
10276 return NULL;
10278 for(i = 0; i < ent_max; i++) {
10279 if(ent_list[i]->exists && testplatform(ent_list[i], x, z)) {
10280 plat = ent_list[i];
10281 if(plat->a <= amax && plat->a + plat->animation->platform[plat->animpos][7] > amin) {
10282 return plat;
10286 return NULL;
10289 //find a lowest platform above this altitude
10290 entity *check_platform_above(float x, float z, float a) {
10291 float mina;
10292 entity *plat = NULL;
10293 int i, ind;
10295 if(level == NULL)
10296 return NULL;
10298 mina = 9999999;
10299 ind = -1;
10300 for(i = 0; i < ent_max; i++) {
10301 if(ent_list[i]->exists && testplatform(ent_list[i], x, z)) {
10302 plat = ent_list[i];
10303 if(plat->a >= a && plat->a < mina) {
10304 mina = plat->a;
10305 ind = i;
10309 return (ind >= 0) ? ent_list[ind] : NULL;
10312 //find a highest platform below this altitude
10313 entity *check_platform_below(float x, float z, float a) {
10314 float maxa;
10315 entity *plat = NULL;
10316 int i, ind;
10318 if(level == NULL)
10319 return NULL;
10321 maxa = 0;
10322 ind = -1;
10323 for(i = 0; i < ent_max; i++) {
10324 if(ent_list[i]->exists && testplatform(ent_list[i], x, z)) {
10325 plat = ent_list[i];
10326 if(plat->a + plat->animation->platform[plat->animpos][7] <= a &&
10327 plat->a + plat->animation->platform[plat->animpos][7] > maxa) {
10328 maxa = plat->a + plat->animation->platform[plat->animpos][7];
10329 ind = i;
10333 return (ind >= 0) ? ent_list[ind] : NULL;
10336 // find the 1st platform entity here
10337 entity *check_platform(float x, float z) {
10338 int i;
10339 if(level == NULL)
10340 return NULL;
10342 for(i = 0; i < ent_max; i++) {
10343 if(ent_list[i]->exists && testplatform(ent_list[i], x, z)) {
10344 return ent_list[i];
10347 return NULL;
10350 // find real opponent
10351 void set_opponent(entity * ent, entity * other) {
10352 entity *realself, *realother;
10354 if(!ent)
10355 return;
10357 realself = ent;
10358 while(realself->owner)
10359 realself = realself->owner;
10361 realother = other;
10362 while(realother && realother->owner)
10363 realother = realother->owner;
10365 realself->opponent = ent->opponent = realother;
10366 if(realother)
10367 realother->opponent = other->opponent = realself;
10372 void do_attack(entity * e) {
10373 int them;
10374 int i;
10375 int force;
10376 entity *temp = NULL;
10377 entity *flash = NULL; // Used so new flashes can be used
10378 entity *def = NULL;
10379 entity *topowner = NULL;
10380 entity *otherowner = NULL;
10381 int didhit = 0;
10382 int didblock = 0; // So a different sound effect can be played when an attack is blocked
10383 int current_attack_id;
10384 int current_follow_id = 0;
10385 int followed = 0;
10386 s_anim *current_anim;
10387 s_attack *attack = e->animation->attacks[e->animpos];
10388 static unsigned int new_attack_id = 1;
10389 int fdefense_blockthreshold = (int) self->modeldata.defense_blockthreshold[(short) attack->attack_type]; //Maximum damage that can be blocked for attack type.
10391 // Can't get hit after this
10392 if(level_completed || !attack)
10393 return;
10395 topowner = e; // trace the top owner, for projectile combo checking :)
10396 while(topowner->owner)
10397 topowner = topowner->owner;
10399 if(e->projectile > 0)
10400 them = e->modeldata.projectilehit;
10401 else
10402 them = e->modeldata.candamage;
10404 // Every attack gets a unique ID to make sure no one
10405 // gets hit more than once by the same attack
10406 current_attack_id = e->attack_id;
10408 if(!current_attack_id) {
10409 ++new_attack_id;
10410 if(new_attack_id == 0)
10411 new_attack_id = 1;
10412 e->attack_id = current_attack_id = new_attack_id;
10415 force = attack->attack_force;
10416 current_anim = e->animation;
10418 for(i = 0; i < ent_max && !followed; i++) {
10420 // if #0
10421 if(ent_list[i]->exists && !ent_list[i]->dead && // dont hit the dead
10422 (ent_list[i]->invincible != 1 || attack->attack_type == ATK_ITEM) && // so invincible people can get items
10423 !(current_anim->attackone > 0 && e->lasthit && ent_list[i] != e->lasthit) && (ent_list[i]->modeldata.type & them) &&
10424 (ent_list[i]->pain_time < borTime || e->animation->fastattack) &&
10425 ent_list[i]->takedamage &&
10426 ent_list[i]->hit_by_attack_id != current_attack_id &&
10428 (ent_list[i]->takeaction != common_lie && attack->otg < 2) ||
10429 (attack->otg >= 1 && ent_list[i]->takeaction == common_lie)
10430 ) && //over the ground hit
10431 ((ent_list[i]->falling == 0 && attack->jugglecost >= 0) || (ent_list[i]->falling == 1 && attack->jugglecost <= ent_list[i]->modeldata.jugglepoints[0])) && // juggle system
10432 (checkhit(e, ent_list[i], 0) || // normal check bbox
10433 (attack->counterattack && checkhit(e, ent_list[i], 1)))) // check counter, e.g. upper
10435 temp = self;
10436 self = ent_list[i];
10438 //Execute on defender.
10439 execute_ondoattack_script(self, e, force, attack->attack_drop, attack->attack_type, attack->no_block,
10440 attack->guardcost, attack->jugglecost, attack->pause_add, 0, current_attack_id);
10441 //Execute on attacker.
10442 execute_ondoattack_script(e, self, force, attack->attack_drop, attack->attack_type, attack->no_block,
10443 attack->guardcost, attack->jugglecost, attack->pause_add, 1, current_attack_id);
10445 if(!lasthitc) {
10446 return;
10447 } //12312010, DC: Allows modder to cancel engine's attack handling. Useful for parry systems, alternate blocking, or other scripted hit events.
10449 didhit = 1;
10451 otherowner = self; // trace top owner for opponent
10452 while(otherowner->owner)
10453 otherowner = otherowner->owner;
10455 //if #01, if they are fired by the same owner, or the owner itself
10456 if(topowner == otherowner)
10457 didhit = 0;
10459 //if #02 , ground missle checking, and bullets wont hit each other
10460 if((e->owner && self->owner) || (e->modeldata.ground && inair(e))) {
10461 didhit = 0;
10462 } //end of if #02
10464 //if #05, blocking code section
10465 if(didhit) {
10466 if(attack->attack_type == ATK_ITEM) {
10467 didfind_item(e);
10468 execute_didhit_script(e, self, force, attack->attack_drop,
10469 self->modeldata.subtype, attack->no_block,
10470 attack->guardcost, attack->jugglecost, attack->pause_add,
10472 return;
10474 //if #051
10475 if(self->toexplode == 1)
10476 self->toexplode = 2; // Used so the bomb type entity explodes when hit
10477 //if #052
10478 if(e->toexplode == 1)
10479 e->toexplode = 2; // Used so the bomb type entity explodes when hitting
10481 if(inair(self))
10482 self->modeldata.jugglepoints[0] = self->modeldata.jugglepoints[0] - attack->jugglecost; //reduce available juggle points.
10484 //if #053
10485 if(!self->modeldata.nopassiveblock && // cant block by itself
10486 validanim(self, ANI_BLOCK) && // of course, move it here to avoid some useless checking
10487 ((self->modeldata.guardpoints[1] == 0) || (self->modeldata.guardpoints[1] > 0 && self->modeldata.guardpoints[0] > 0)) && !(self->link || inair(self) || self->frozen || (self->direction == e->direction && self->modeldata.blockback < 1) || // Can't block an attack that is from behind unless blockback flag is enabled
10488 (!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
10489 attack->no_block <= self->modeldata.defense_blockpower[(short) attack->attack_type] && // If unblockable, will automatically hit
10490 (rand32() & self->modeldata.blockodds) == 1 && // Randomly blocks depending on blockodds (1 : blockodds ratio)
10491 (!self->modeldata.thold || (self->modeldata.thold > 0 && self->modeldata.thold > force)) && (!fdefense_blockthreshold || //Specific attack type threshold.
10492 (fdefense_blockthreshold > force))) { //execute the didhit script
10493 execute_didhit_script(e, self, force, attack->attack_drop, attack->attack_type,
10494 attack->no_block, attack->guardcost, attack->jugglecost,
10495 attack->pause_add, 1);
10496 set_blocking(self);
10497 self->xdir = self->zdir = 0;
10498 ent_set_anim(self, ANI_BLOCK, 0);
10499 self->takeaction = common_block;
10500 execute_didblock_script(self, e, force, attack->attack_drop,
10501 attack->attack_type, attack->no_block,
10502 attack->guardcost, attack->jugglecost,
10503 attack->pause_add);
10504 if(self->modeldata.guardpoints[1] > 0)
10505 self->modeldata.guardpoints[0] =
10506 self->modeldata.guardpoints[0] - attack->guardcost;
10507 ++e->animation->animhits;
10508 didblock = 1; // Used for when playing the block.wav sound
10509 // Spawn a flash
10510 //if #0531
10511 if(!attack->no_flash) {
10512 if(!self->modeldata.noatflash) {
10513 if(attack->blockflash >= 0)
10514 flash = spawn(lasthitx, lasthitz, lasthita, 0, NULL, attack->blockflash, NULL); // custom bflash
10515 else
10516 flash = spawn(lasthitx, lasthitz, lasthita, 0, NULL, ent_list[i]->modeldata.bflash, NULL); // New block flash that can be smaller
10517 } else
10518 flash =
10519 spawn(lasthitx, lasthitz, lasthita, 0, NULL,
10520 self->modeldata.bflash, NULL);
10521 //ent_default_init(flash); // initiliaze this because there're no default values now
10523 if(flash)
10524 execute_onspawn_script(flash);
10526 //end of if #0531
10527 } else if(self->modeldata.nopassiveblock && // can block by itself
10528 self->blocking && // of course he must be blocking
10529 ((self->modeldata.guardpoints[1] == 0) || (self->modeldata.guardpoints[1] > 0 && self->modeldata.guardpoints[0] > 0)) && !((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
10530 attack->no_block <= self->modeldata.defense_blockpower[(short) attack->attack_type] && // Make sure you are actually blocking and that the attack is blockable
10531 (!self->modeldata.thold || (self->modeldata.thold > 0 && self->modeldata.thold > force)) && (!self->modeldata.defense_blockthreshold[(short) attack->attack_type] || //Specific attack type threshold.
10532 (self->modeldata.defense_blockthreshold[(short) attack->attack_type] > force))) { // Only block if the attack is less than the players threshold
10533 //execute the didhit script
10534 execute_didhit_script(e, self, force, attack->attack_drop, attack->attack_type,
10535 attack->no_block, attack->guardcost, attack->jugglecost,
10536 attack->pause_add, 1);
10537 if(self->modeldata.guardpoints[1] > 0)
10538 self->modeldata.guardpoints[0] =
10539 self->modeldata.guardpoints[0] - attack->guardcost;
10540 ++e->animation->animhits;
10541 didblock = 1; // Used for when playing the block.wav sound
10543 if(self->modeldata.blockpain && self->modeldata.blockpain <= force && self->animation == self->modeldata.animation[ANI_BLOCK]) //Blockpain 1 and in block animation?
10545 set_blockpain(self, attack->attack_type, 0);
10547 execute_didblock_script(self, e, force, attack->attack_drop,
10548 attack->attack_type, attack->no_block,
10549 attack->guardcost, attack->jugglecost,
10550 attack->pause_add);
10552 // Spawn a flash
10553 if(!attack->no_flash) {
10554 if(!self->modeldata.noatflash) {
10555 if(attack->blockflash >= 0)
10556 flash = spawn(lasthitx, lasthitz, lasthita, 0, NULL, attack->blockflash, NULL); // custom bflash
10557 else
10558 flash = spawn(lasthitx, lasthitz, lasthita, 0, NULL, ent_list[i]->modeldata.bflash, NULL); // New block flash that can be smaller
10559 } else
10560 flash =
10561 spawn(lasthitx, lasthitz, lasthita, 0, NULL,
10562 self->modeldata.bflash, NULL);
10563 //ent_default_init(flash); // initiliaze this because there're no default values now
10564 if(flash)
10565 execute_onspawn_script(flash);
10567 } else if((self->animpos >= self->animation->counterframe[0] && self->animpos <= self->animation->counterframe[1]) && //Within counter range?
10568 !self->frozen) // && //Not frozen?
10569 //(self->animation->counterframe[2] <= 1 && e->modeldata.type & them)) //&& //Friend/foe?
10570 //(self->animation->counterframe[2] <= 3 && !attack->no_block) && //Counter attack self couldn't block?
10571 //self->animation->counterframe[2] <= 2 ||
10572 //self->animation->counterframe[2] <= 2 || !(self->direction == e->direction)) //&& //Direction check.
10573 //(self->animation->counterframe[2] <= 3 || !attack->freeze)) //Freeze attacks?
10575 //&& (!self->animation->counterframe[3] || self->health > force)) // Does damage matter?
10577 if(self->animation->counterframe[3])
10578 self->health -= force; // Take damage?
10579 current_follow_id = dyn_anims.animfollows[self->animation->followanim - 1];
10580 if(validanim(self, current_follow_id)) {
10581 if(self->modeldata.animation[current_follow_id]->attackone == -1)
10582 self->modeldata.animation[current_follow_id]->attackone =
10583 self->animation->attackone;
10584 ent_set_anim(self, current_follow_id, 0);
10585 self->hit_by_attack_id = current_attack_id;
10588 if(!attack->no_flash) {
10589 if(!self->modeldata.noatflash) {
10590 if(attack->blockflash >= 0)
10591 flash = spawn(lasthitx, lasthitz, lasthita, 0, NULL, attack->blockflash, NULL); // custom bflash
10592 else
10593 flash = spawn(lasthitx, lasthitz, lasthita, 0, NULL, ent_list[i]->modeldata.bflash, NULL); // New block flash that can be smaller
10594 } else
10595 flash =
10596 spawn(lasthitx, lasthitz, lasthita, 0, NULL,
10597 self->modeldata.bflash, NULL);
10598 //ent_default_init(flash); // initiliaze this because there're no default values now
10599 if(flash)
10600 execute_onspawn_script(flash);
10602 } else if(self->takedamage(e, attack)) { // Didn't block so go ahead and take the damage
10603 //printf("*%d*", current_attack_id);
10604 execute_didhit_script(e, self, force, attack->attack_drop, attack->attack_type,
10605 attack->no_block, attack->guardcost, attack->jugglecost,
10606 attack->pause_add, 0);
10607 ++e->animation->animhits;
10609 e->lasthit = self;
10611 // Spawn a flash
10612 if(!attack->no_flash) {
10613 if(!self->modeldata.noatflash) {
10614 if(attack->hitflash >= 0)
10615 flash =
10616 spawn(lasthitx, lasthitz, lasthita, 0, NULL,
10617 attack->hitflash, NULL);
10618 else
10619 flash =
10620 spawn(lasthitx, lasthitz, lasthita, 0, NULL,
10621 e->modeldata.flash, NULL);
10622 } else
10623 flash =
10624 spawn(lasthitx, lasthitz, lasthita, 0, NULL,
10625 self->modeldata.flash, NULL);
10626 if(flash)
10627 execute_onspawn_script(flash);
10629 topowner->combotime = borTime + combodelay; // well, add to its owner's combo
10631 if(e->animpos != e->lastanimpos || (inair(e) && !equalairpause)) // if equalairpause is set, inair(e) is nolonger a condition for extra pausetime
10632 { // Adds pause to the current animation
10633 e->toss_time += attack->pause_add; // So jump height pauses in midair
10634 e->nextanim += attack->pause_add; //Pause animation for a bit
10635 e->nextthink += attack->pause_add; // So anything that auto moves will pause
10638 e->lastanimpos = e->animpos;
10640 self->toss_time += attack->pause_add; // So jump height pauses in midair
10641 self->nextanim += attack->pause_add; //Pause animation for a bit
10642 self->nextthink += attack->pause_add; // So anything that auto moves will pause
10644 } else {
10645 didhit = 0;
10646 continue;
10648 // end of if #053
10650 // if #054
10651 if(flash && !attack->no_flash) {
10652 if(flash->modeldata.toflip)
10653 flash->direction = (e->x > self->x); // Now the flash will flip depending on which side the attacker is on
10655 flash->base = lasthita;
10656 flash->autokill = 1;
10657 } //end of if #054
10659 // 2007 3 24, hmm, def should be like this
10660 if(didblock && !def)
10661 def = self;
10662 //if #055
10663 if((e->animation->followanim) && // follow up?
10664 (e->animation->counterframe[0] == -1) && // This isn't suppossed to be a counter, right?
10665 ((e->animation->followcond < 2) || (self->modeldata.type & them)) && // Does type matter?
10666 ((e->animation->followcond < 3) || ((self->health > 0) && !didblock)) && // check if health or blocking matters
10667 ((e->animation->followcond < 4) || cangrab(e, self))) // check if nograb matters
10669 current_follow_id = dyn_anims.animfollows[e->animation->followanim - 1];
10670 if(validanim(e, current_follow_id)) {
10671 if(e->modeldata.animation[current_follow_id]->attackone == -1)
10672 e->modeldata.animation[current_follow_id]->attackone =
10673 e->animation->attackone;
10674 ent_set_anim(e, current_follow_id, 1); // Then go to it!
10676 followed = 1; // quit loop, animation is changed
10677 } //end of if #055
10679 self->hit_by_attack_id = current_attack_id;
10680 if(self == def)
10681 self->blocking = didblock; // yeah, if get hit, stop blocking
10682 } //end of if #05
10683 self = temp;
10684 } //end of if #0
10686 } //end of for
10689 // if ###
10690 if(didhit) {
10691 // well, dont check player or not - UTunnels. TODO: take care of that healthcheat
10692 if(e == topowner && current_anim->energycost[0] > 0 && nocost && !healthcheat)
10693 e->tocost = 1; // Set flag so life is subtracted when animation is finished
10694 else if(e != topowner && current_anim->energycost[0] > 0 && nocost && !healthcheat && !e->tocost) // if it is not top, then must be a shot
10696 if(current_anim->energycost[1] != 2 && topowner->mp > 0) {
10697 topowner->mp -= current_anim->energycost[0];
10698 if(topowner->mp < 0)
10699 topowner->mp = 0;
10700 } else {
10701 topowner->health -= current_anim->energycost[0];
10702 if(topowner->health <= 0)
10703 topowner->health = 1;
10706 topowner->cantfire = 0; // Life subtracted, so go ahead and allow firing
10707 e->tocost = 1; // Little backwards, but set to 1 so cost doesn't get subtracted multiple times
10709 // New blocking checks
10710 //04/27/2008 Damon Caskey: Added checks for defense property specfic blockratio and type. Could probably use some cleaning.
10711 if(didblock) {
10712 if(blockratio || def->modeldata.defense_blockratio[(short) attack->attack_type]) // Is damage reduced?
10714 if(def->modeldata.defense_blockratio[(short) attack->attack_type]) { //Typed blockratio?
10715 force =
10716 (int) (force *
10717 def->modeldata.defense_blockratio[(short) attack->attack_type]);
10718 } else { //No typed. Use static block ratio.
10719 force = force / 4;
10722 if(mpblock && !def->modeldata.defense_blocktype[(short) attack->attack_type]) { // Drain MP bar first?
10723 def->mp -= force;
10724 if(def->mp < 0) {
10725 force = -def->mp;
10726 def->mp = 0;
10727 } else
10728 force = 0; // Damage removed from MP!
10729 } else if(def->modeldata.defense_blocktype[(short) attack->attack_type] == 1) { //Damage from MP only for this attack type.
10730 def->mp -= force;
10731 if(def->mp < 0) {
10732 force = -def->mp;
10733 def->mp = 0;
10734 } else
10735 force = 0; // Damage removed from MP!
10736 } else if(def->modeldata.defense_blocktype[(short) attack->attack_type] == 2) { //Damage from both HP and MP at once.
10737 def->mp -= force;
10738 } else if(def->modeldata.defense_blocktype[(short) attack->attack_type] == -1) { //Health only?
10739 //Do nothing. This is so modders can overidde energycost[1] 1 with health only.
10742 if(force < def->health) // If an attack won't deal damage, this line won't do anything anyway.
10743 def->health -= force;
10744 else if(nochipdeath) // No chip deaths?
10745 def->health = 1;
10746 else {
10747 temp = self;
10748 self = def;
10749 self->takedamage(e, attack); // Must be a fatal attack, then!
10750 self = temp;
10755 if(!didblock) {
10756 topowner->rushtime = borTime + (GAME_SPEED * rush[1]);
10757 topowner->rush[0]++;
10758 if(topowner->rush[0] > topowner->rush[1] && topowner->rush[0] > 1)
10759 topowner->rush[1] = topowner->rush[0];
10762 if(didblock) {
10763 if(attack->blocksound >= 0)
10764 sound_play_sample(attack->blocksound, 0, savedata.effectvol, savedata.effectvol, 100); // New custom block sound effect
10765 else
10766 sound_play_sample(samples.block, 0, savedata.effectvol, savedata.effectvol, 100); // Default block sound effect
10767 } else if(e->projectile > 0)
10768 sound_play_sample(samples.indirect, 0, savedata.effectvol, savedata.effectvol, 100);
10769 else {
10770 if(noslowfx) {
10771 if(attack->hitsound >= 0)
10772 sound_play_sample(attack->hitsound, 0, savedata.effectvol, savedata.effectvol,
10773 100);
10774 else
10775 sound_play_sample(samples.beat, 0, savedata.effectvol, savedata.effectvol, 100);
10776 } else {
10777 if(attack->hitsound >= 0)
10778 sound_play_sample(attack->hitsound, 0, savedata.effectvol, savedata.effectvol,
10779 105 - force);
10780 else
10781 sound_play_sample(samples.beat, 0, savedata.effectvol, savedata.effectvol,
10782 105 - force);
10786 if(e->remove_on_attack)
10787 kill(e);
10788 } //end of if ###
10792 void check_gravity() {
10793 int heightvar;
10794 entity *other, *dust;
10795 s_attack attack;
10797 if(!is_frozen(self)) // Incase an entity is in the air, don't update animations
10799 if((self->falling || self->tossv || self->a != self->base) && self->toss_time <= borTime
10800 && !self->animation->dive[0] && !self->animation->dive[1]) {
10801 if(self->modeldata.subject_to_platform > 0 && self->tossv > 0)
10802 other = check_platform_above(self->x, self->z, self->a + self->tossv);
10803 else
10804 other = NULL;
10806 if(self->animation->height)
10807 heightvar = self->animation->height;
10808 else
10809 heightvar = self->modeldata.height;
10811 if(other && other->a <= self->a + heightvar) {
10812 if(self->hithead == NULL) // bang! Hit the ceiling.
10814 self->tossv = 0;
10815 self->hithead = other;
10816 execute_onblocka_script(self, other);
10818 } else
10819 self->hithead = NULL;
10820 // gravity, antigravity factors
10821 self->a += self->tossv;
10822 if(self->modeldata.subject_to_gravity > 0)
10823 self->tossv += level->gravity * (1.0 - self->modeldata.antigravity - self->antigravity);
10825 if(self->tossv < level->maxfallspeed) {
10826 self->tossv = level->maxfallspeed;
10827 } else if(self->tossv > level->maxtossspeed) {
10828 self->tossv = level->maxtossspeed;
10830 if(self->animation->dropframe >= 0 && self->tossv <= 0 && self->animpos < self->animation->dropframe) // begin dropping
10832 update_frame(self, self->animation->dropframe);
10834 if(self->tossv)
10835 execute_onmovea_script(self); //Move A event.
10837 if(self->idling && validanim(self, ANI_WALKOFF)) {
10838 self->idling = 0;
10839 self->takeaction = common_walkoff;
10840 ent_set_anim(self, ANI_WALKOFF, 0);
10842 // UTunnels: tossv <= 0 means land, while >0 means still rising, so
10843 // you wont be stopped if you are passing the edge of a wall
10844 if((self->a <= self->base || !inair(self)) && self->tossv <= 0) {
10845 self->a = self->base;
10846 self->falling = 0;
10847 //self->projectile = 0;
10848 // cust dust entity
10849 if(self->modeldata.dust[0] >= 0 && self->tossv < -1 && self->drop) {
10850 dust =
10851 spawn(self->x, self->z, self->a, self->direction, NULL,
10852 self->modeldata.dust[0], NULL);
10853 dust->base = self->a;
10854 dust->autokill = 1;
10855 execute_onspawn_script(dust);
10857 // bounce/quake
10858 if(tobounce(self) && self->modeldata.bounce) {
10859 int i;
10860 self->xdir /= self->animation->bounce;
10861 self->zdir /= self->animation->bounce;
10862 toss(self, (-self->tossv) / self->animation->bounce);
10863 if(!self->modeldata.noquake)
10864 level->quake = 4; // Don't shake if specified
10865 sound_play_sample(samples.fall, 0, savedata.effectvol,
10866 savedata.effectvol, 100);
10867 if(self->modeldata.type == TYPE_PLAYER)
10868 control_rumble(self->playerindex, 100 * (int) self->tossv / 2);
10869 for(i = 0; i < MAX_PLAYERS; i++)
10870 control_rumble(i, 75 * (int) self->tossv / 2);
10871 } else if((!self->animation->seta || self->animation->seta[self->animpos] < 0) &&
10872 (!self->animation->movea || self->animation->movea[self->animpos] <= 0))
10873 self->xdir = self->zdir = self->tossv = 0;
10874 else
10875 self->tossv = 0;
10877 if(self->animation->landframe[0] >= 0 //Has landframe?
10878 && self->animation->landframe[0] <= self->animation->numframes //Not over animation frame count?
10879 && self->animpos < self->animation->landframe[0]) //Not already past landframe?
10881 update_frame(self, self->animation->landframe[0]);
10882 if(self->animation->landframe[1] >= 0) {
10883 dust =
10884 spawn(self->x, self->z, self->a, self->direction, NULL,
10885 self->animation->landframe[1], NULL);
10886 dust->base = self->a;
10887 dust->autokill = 1;
10888 execute_onspawn_script(dust);
10891 // takedamage if thrown or basted
10892 if(self->damage_on_landing > 0 && !self->dead) {
10893 if(self->takedamage)
10895 attack = emptyattack;
10896 attack.attack_force = self->damage_on_landing;
10897 attack.attack_type = ATK_NORMAL;
10898 self->takedamage(self, &attack);
10899 } else {
10900 self->health -= self->damage_on_landing;
10901 if(self->health <= 0)
10902 kill(self);
10903 self->damage_on_landing = 0;
10906 // in case landing, set hithead to NULL
10907 self->hithead = NULL;
10908 } // end of if - land checking
10909 self->toss_time = borTime + (GAME_SPEED / 100);
10910 } // end of if - in-air checking
10912 } //end of if
10915 void check_lost() {
10916 s_attack attack;
10917 int osk = self->modeldata.offscreenkill ? self->modeldata.offscreenkill : DEFAULT_OFFSCREEN_KILL;
10919 if((self->z != 100000 && (advancex - self->x > osk || self->x - advancex - videomodes.hRes > osk ||
10920 (level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN
10921 && (advancey - self->z > osk || self->z - advancey - videomodes.vRes > osk))
10922 || ((level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN)
10923 && (self->z < -osk || self->z > videomodes.vRes + osk))))
10924 || self->a < 2 * PIT_DEPTH) //self->z<100000, so weapon item won't be killed
10926 if(self->modeldata.type == TYPE_PLAYER)
10927 player_die();
10928 else
10929 kill(self);
10930 return;
10932 // fall in to a pit
10933 if(self->a < PIT_DEPTH || self->lifespancountdown < 0) {
10934 if(!self->takedamage)
10935 kill(self);
10936 else {
10937 attack = emptyattack;
10938 attack.dropv[0] = (float) 3;
10939 attack.dropv[1] = (float) 1.2;
10940 attack.dropv[2] = (float) 0;
10941 attack.attack_force = self->health;
10942 attack.attack_type = dyn_anim_custom_maxvalues.max_attack_types;
10943 self->takedamage(self, &attack);
10945 return;
10946 } //else
10947 // Doom count down
10948 if(!is_frozen(self) && self->lifespancountdown != (float) 0xFFFFFFFF)
10949 self->lifespancountdown--;
10952 // grab walk check
10953 void check_link_move(float xdir, float zdir) {
10954 float x, z, gx, gz;
10955 int tryresult;
10956 entity *tempself = self;
10957 gx = self->grabbing->x;
10958 gz = self->grabbing->z;
10959 x = self->x;
10960 z = self->z;
10961 self = self->grabbing;
10962 tryresult = self->trymove(xdir, zdir);
10963 self = tempself;
10964 if(tryresult != 1) // changed
10966 xdir = self->grabbing->x - gx;
10967 zdir = self->grabbing->z - gz;
10969 tryresult = self->trymove(xdir, zdir);
10970 if(tryresult != 1) {
10971 self->grabbing->x = self->x - x + gx;
10972 self->grabbing->z = self->z - z + gz;
10976 void check_ai() {
10977 entity *plat;
10978 // check moving platform
10979 if((plat = self->landed_on_platform) && (plat->xdir || plat->zdir) && (plat->nextthink <= borTime || (plat->update_mark & 2)) && // plat is updated before self or will be updated this loop
10980 testplatform(plat, self->x, self->z) && self->a <= plat->a + plat->animation->platform[plat->animpos][7] + 0.5) // on the platform?
10982 // passive move with the platform
10983 if(self->trymove) {
10984 // grab walk check
10985 if(self->grabbing && self->grabwalking && self->grabbing->trymove) {
10986 check_link_move(plat->xdir, plat->zdir);
10987 } else
10988 self->trymove(plat->xdir, plat->zdir);
10989 } else {
10990 self->x += plat->xdir;
10991 self->z += plat->zdir;
10995 if(self->nextthink <= borTime && !endgame) {
10996 self->update_mark |= 2; //mark it
10997 // take actions
10998 if(self->takeaction)
10999 self->takeaction();
11001 // A.I. think
11002 if(self->think) {
11003 if(self->nextthink <= borTime)
11004 self->nextthink = borTime + THINK_SPEED;
11005 // use noaicontrol flag to turn of A.I. think
11006 if(!self->noaicontrol)
11007 self->think();
11009 // Execute think script
11010 execute_think_script(self);
11012 // A.I. move
11013 if(self->xdir || self->zdir) {
11014 if(self->trymove) {
11015 // grab walk check
11016 if(self->grabbing && self->grabwalking && self->grabbing->trymove) {
11017 check_link_move(self->xdir, self->zdir);
11018 } else if(self->trymove(self->xdir, self->zdir) != 1 && self->idling) {
11019 self->pathblocked++; // for those who walk against wall or borders
11021 } else {
11022 self->x += self->xdir;
11023 self->z += self->zdir;
11026 // Used so all entities can have a spawn animation, and then just changes to the idle animation when done
11027 // move here to so players wont get stuck
11028 if((self->animation == self->modeldata.animation[ANI_SPAWN]
11029 || self->animation == self->modeldata.animation[ANI_RESPAWN])
11030 && !self->animating /*&& (!inair(self)||!self->modeldata.subject_to_gravity) */ )
11031 set_idle(self);
11036 void update_animation() {
11037 int f, wall, hole;
11038 float move, movez, seta;
11039 entity *other = NULL;
11041 if(level) {
11042 if(self->modeldata.facing == 1 || level->facing == 1)
11043 self->direction = 1;
11044 else if(self->modeldata.facing == 2 || level->facing == 2)
11045 self->direction = 0;
11046 else if((self->modeldata.facing == 3 || level->facing == 3) && (level->scrolldir & SCROLL_RIGHT))
11047 self->direction = 1;
11048 else if((self->modeldata.facing == 3 || level->facing == 3) && (level->scrolldir & SCROLL_LEFT))
11049 self->direction = 0;
11050 if(self->modeldata.type == TYPE_PANEL) {
11051 self->x += scrolldx * ((float) (self->modeldata.speed));
11052 if(level->scrolldir == SCROLL_UP) {
11053 self->a += scrolldy * ((float) (self->modeldata.speed));
11054 } else if(level->scrolldir == SCROLL_DOWN) {
11055 self->a -= scrolldy * ((float) (self->modeldata.speed));
11056 } else {
11057 self->a -= scrolldy * ((float) (self->modeldata.speed));
11060 if(self->modeldata.scroll) {
11061 self->x += scrolldx * ((float) (self->modeldata.scroll));
11062 if(level->scrolldir == SCROLL_UP) {
11063 self->a += scrolldy * ((float) (self->modeldata.scroll));
11064 } else if(level->scrolldir == SCROLL_DOWN) {
11065 self->a -= scrolldy * ((float) (self->modeldata.scroll));
11066 } else {
11067 self->a -= scrolldy * ((float) (self->modeldata.scroll));
11072 if(self->invincible && borTime >= self->invinctime) // Invincible time has run out, turn off
11074 self->invincible = 0;
11075 self->blink = 0;
11076 self->invinctime = 0;
11077 self->arrowon = 0;
11080 if(self->dying) // Code for doing dying flash
11082 if((self->health <= self->per1 && self->health > self->per2
11083 && (borTime % (GAME_SPEED / 10)) < (GAME_SPEED / 40)) || (self->health <= self->per2)) {
11084 if(self->colourmap == self->modeldata.colourmap[self->dying - 1] || self->health <= 0) {
11085 ent_set_colourmap(self, self->map);
11086 } else {
11087 self->colourmap = self->modeldata.colourmap[self->dying - 1];
11092 if(self->freezetime && borTime >= self->freezetime) {
11093 unfrozen(self);
11096 if(self->maptime && borTime >= self->maptime) {
11097 ent_set_colourmap(self, self->map);
11100 if(self->sealtime && borTime >= self->sealtime) //Remove seal, special moves are available again.
11102 self->seal = 0;
11105 if(self->nextanim == borTime || (self->modeldata.type == TYPE_TEXTBOX && self->modeldata.subtype != SUBTYPE_NOSKIP && (bothnewkeys & (FLAG_JUMP | FLAG_ATTACK | FLAG_ATTACK2 | FLAG_ATTACK3 | FLAG_ATTACK4 | FLAG_SPECIAL)))) // Textbox will autoupdate if a valid player presses an action button
11106 { // Now you can display text and cycle through with any jump/attack/special unless SUBTYPE_NOSKIP
11108 f = self->animpos + self->animating;
11110 //Specified loop break frame.
11111 if(self->animation->loop[0] && self->animation->loop[2]) {
11112 if(f == self->animation->loop[2]) {
11113 if(f < 0)
11114 f = self->animation->numframes - 1;
11115 else
11116 f = 0;
11118 if(self->animation->loop[1]) {
11119 f = self->animation->loop[1];
11121 } else if((unsigned) f >= (unsigned) self->animation->numframes) {
11122 self->animating = 0;
11124 if(self->autokill) {
11125 kill(self);
11126 return;
11129 } else if((unsigned) f >= (unsigned) self->animation->numframes) {
11130 if(f < 0)
11131 f = self->animation->numframes - 1;
11132 else
11133 f = 0;
11135 if(!self->animation->loop[0]) {
11136 self->animating = 0;
11138 if(self->autokill) {
11139 kill(self);
11140 return;
11142 } else {
11143 if(self->animation->loop[1]) {
11144 f = self->animation->loop[1];
11149 if(self->animating) {
11150 //self->nextanim = borTime + (self->animation->delay[f]);
11151 self->update_mark |= 1; // frame updated, mark it
11152 // just switch frame to f, if frozen, expand_time will deal with it well
11153 update_frame(self, f);
11157 if(self->modeldata.subject_to_platform > 0 /*&& self->projectile==0 */ ) {
11158 other = self->landed_on_platform;
11159 if(other && testplatform(other, self->x, self->z)
11160 && self->a < other->a + other->animation->platform[other->animpos][7]) {
11161 self->a = self->base = other->a + other->animation->platform[other->animpos][7] + 0.5;
11162 } else
11163 other = check_platform_below(self->x, self->z, self->a + 1);
11164 } else
11165 other = NULL;
11166 self->landed_on_platform = other;
11167 // adjust base
11168 if(self->modeldata.no_adjust_base <= 0) {
11169 seta = (float) ((self->animation->seta) ? (self->animation->seta[self->animpos]) : (-1));
11171 // Checks to see if entity is over a wall and or obstacle, and adjusts the base accordingly
11172 //wall = checkwall_below(self->x, self->z);
11173 //find a wall below us
11174 if(self->modeldata.subject_to_wall > 0)
11175 wall = checkwall_below(self->x, self->z, self->a);
11176 else
11177 wall = -1;
11179 if(self->modeldata.subject_to_hole > 0) {
11180 hole = (wall < 0 && !other) ? checkhole(self->x, self->z) : 0;
11182 if(seta < 0 && hole) {
11183 self->base = -1000;
11184 ent_unlink(self);
11185 } else if(!hole && self->base == -1000) {
11186 if(self->a >= 0)
11187 self->base = 0;
11188 else {
11189 self->xdir = self->zdir = 0; // hit the hole border
11194 if(self->base != -1000 || wall >= 0) {
11195 if(other != NULL && other != self) {
11196 self->base =
11197 (seta + self->altbase >=
11198 0) * (seta + self->altbase) + (other->a +
11199 other->animation->platform[other->animpos][7]);
11200 } else if(wall >= 0) {
11201 //self->modeldata.subject_to_wall &&//we move this up to avoid some checking time
11202 self->base =
11203 (seta + self->altbase >= 0) * (seta + self->altbase) + (self->a >=
11204 level->walls[wall].alt) *
11205 level->walls[wall].alt;
11206 } else if(seta >= 0)
11207 self->base = (seta + self->altbase >= 0) * (seta + self->altbase);
11208 else if(self->animation != self->modeldata.animation[ANI_VAULT]
11209 && (!self->animation->movea || self->animation->movea[self->animpos] == 0)) {
11210 // Don't want to adjust the base if vaulting
11211 // No obstacle/wall or seta, so just set to 0
11212 self->base = 0;
11216 // Code for when entities move (useful for moving platforms, etc)
11217 if(other && other != self) {
11218 // a bit complex, other->nextanim == time means other is behind self and not been updated,
11219 // update_mark & 1 means other is updated in this loop and before self
11220 if((other->nextanim == borTime || (other->update_mark & 1))
11221 && self->a <= other->a + other->animation->platform[other->animpos][7] + 0.5) {
11222 if(other->update_mark & 1)
11223 f = other->animpos;
11224 else
11225 f = other->animpos + other->animating;
11226 if(f >= other->animation->numframes) {
11227 if(f < 0)
11228 f = other->animation->numframes - 1;
11229 else
11230 f = 0;
11232 //printf("%d %d %d\n", other->nextanim, borTime, other->update_mark);
11233 move = (float) (other->animation->move ? other->animation->move[f] : 0);
11234 movez = (float) (other->animation->movez ? other->animation->movez[f] : 0);
11235 if(other->direction == 0)
11236 move = -move;
11237 if(move || movez) {
11238 if(self->trymove) {
11239 self->trymove(move, movez);
11240 } else {
11241 self->z += movez;
11242 self->x += move;
11249 void check_attack() {
11250 // a normal fall
11251 if(self->falling && !self->projectile) {
11252 self->attack_id = 0;
11253 return;
11255 // on ground
11256 if(self->drop && !self->falling) {
11257 self->attack_id = 0;
11258 return;
11260 // Can't hit an opponent if you are frozen
11261 if(!is_frozen(self) && self->animation->attacks && self->animation->attacks[self->animpos]) {
11262 do_attack(self);
11263 return;
11265 self->attack_id = 0;
11269 void update_health() {
11270 //12/30/2008: Guardrate by OX. Guardpoints increase over time.
11271 if(self->modeldata.guardpoints[1] > 0 && borTime >= self->guardtime) // If this is > 0 then guardpoints are set..
11273 if(self->blocking) {
11274 self->modeldata.guardpoints[0] += (self->modeldata.guardrate / 2);
11275 if(self->modeldata.guardpoints[0] > self->modeldata.guardpoints[1])
11276 self->modeldata.guardpoints[0] = self->modeldata.guardpoints[1];
11277 } else {
11278 self->modeldata.guardpoints[0] += self->modeldata.guardrate;
11279 if(self->modeldata.guardpoints[0] > self->modeldata.guardpoints[1])
11280 self->modeldata.guardpoints[0] = self->modeldata.guardpoints[1];
11282 self->guardtime = borTime + GAME_SPEED; //Reset guardtime.
11285 common_dot(); //Damage over time.
11287 // this is for restoring mp by time by tails
11288 // Cleaning and addition of mpstable by DC, 08172008.
11289 // stabletype 4 added by OX 12272008
11290 if(magic_type == 0 && !self->charging) {
11291 if(borTime >= self->magictime) {
11293 // 1 Only recover MP > mpstableval.
11294 // 2 No recover. Drop MP if MP < mpstableval.
11295 // 3 Both: recover if MP if MP < mpstableval and drop if MP > mpstableval.
11296 // 0 Default. Recover MP at all times.
11297 // 4. Gain mp until it reaches max. Then it drops down to mpstableval.
11298 switch(self->modeldata.mpstable) {
11299 case 1:
11300 if(self->mp < self->modeldata.mpstableval)
11301 self->mp += self->modeldata.mprate;
11302 break;
11303 case 2:
11304 if(self->mp > self->modeldata.mpstableval)
11305 self->mp -= self->modeldata.mpdroprate;
11306 break;
11307 case 3:
11308 if(self->mp < self->modeldata.mpstableval) {
11310 self->mp += self->modeldata.mprate;
11311 } else if(self->mp > self->modeldata.mpstableval) {
11312 self->mp -= self->modeldata.mpdroprate;
11314 break;
11315 case 4:
11316 if(self->mp <= self->modeldata.mpstableval)
11317 self->modeldata.mpswitch = 0;
11318 else if(self->mp == self->modeldata.mp)
11319 self->modeldata.mpswitch = 1;
11321 if(self->modeldata.mpswitch == 1) {
11322 self->mp -= self->modeldata.mpdroprate;
11323 } else if(self->modeldata.mpswitch == 0) {
11324 self->mp += self->modeldata.mprate;
11326 break;
11327 default:
11328 self->mp += self->modeldata.mprate;
11330 self->magictime = borTime + GAME_SPEED; //Reset magictime.
11333 if(self->charging && borTime >= self->mpchargetime) {
11334 self->mp += self->modeldata.chargerate;
11335 self->mpchargetime = borTime + (GAME_SPEED / 4);
11337 if(self->mp > self->modeldata.mp)
11338 self->mp = self->modeldata.mp; // Don't want to add more than the max
11340 if(self->oldhealth < self->health)
11341 self->oldhealth++;
11342 else if(self->oldhealth > self->health)
11343 self->oldhealth--;
11345 if(self->oldmp < self->mp)
11346 self->oldmp++;
11347 else if(self->oldmp > self->mp)
11348 self->oldmp--;
11351 void common_dot() {
11352 //common_dot
11353 //Damon V. Caskey
11354 //06172009
11355 //Mitigates damage over time (dot). Moved here from update_health().
11357 int iFForce; //Final force; total damage after defense and offense factors are applied.
11358 int iType; //Attack type.
11359 int iIndex; //Dot index.
11360 int iDot; //Dot mode.
11361 int iDot_time; //Dot expire time.
11362 int iDot_cnt; //Dot next tick time.
11363 int iDot_rate; //Dot tick rate.
11364 int iForce; //Unmodified force.
11365 float fOffense; //Owner's offense.
11366 float fDefense; //Self defense.
11367 entity *eOpp; //Owner of dot effect.
11368 s_attack attack; //Attack struct.
11370 for(iIndex = 0; iIndex < MAX_DOTS; iIndex++) //Loop through all DOT indexes.
11372 iDot_time = self->dot_time[iIndex]; //Get expire time.
11373 iDot_cnt = self->dot_cnt[iIndex]; //Get next tick time.
11374 iDot_rate = self->dot_rate[iIndex]; //Get tick rate.
11376 if(iDot_time) //Dot time present?
11378 if(borTime > iDot_time) //Dot effect expired? Then clear variants.
11380 self->dot[iIndex] = 0;
11381 self->dot_atk[iIndex] = 0;
11382 self->dot_cnt[iIndex] = 0;
11383 self->dot_rate[iIndex] = 0;
11384 self->dot_time[iIndex] = 0;
11385 self->dot_force[iIndex] = 0;
11386 } else if(borTime >= iDot_cnt && self->health >= 0) //Time for a dot tick and alive?
11388 self->dot_cnt[iIndex] = borTime + (iDot_rate * GAME_SPEED / 100); //Reset next tick time.
11390 iDot = self->dot[iIndex]; //Get dot mode.
11391 iForce = self->dot_force[iIndex]; //Get dot force.
11393 if(iDot == 1 || iDot == 3 || iDot == 4 || iDot == 5) //HP?
11395 eOpp = self->dot_owner[iIndex]; //Get dot effect owner.
11396 iType = self->dot_atk[iIndex]; //Get attack type.
11397 iFForce = iForce; //Initialize final force.
11398 fOffense = eOpp->modeldata.offense_factors[iType]; //Get owner's offense.
11399 fDefense = self->modeldata.defense_factors[iType]; //Get Self defense.
11401 if(fOffense) {
11402 iFForce = (int) (iForce * fOffense);
11403 } //Apply offense factors.
11404 if(fDefense) {
11405 iFForce = (int) (iFForce * fDefense);
11406 } //Apply defense factors.
11408 if(iFForce >= self->health && (iDot == 4 || iDot == 5)) //Total force lethal?
11410 attack = emptyattack; //Clear struct.
11411 attack.attack_type = iType; //Set type.
11412 attack.attack_force = iForce; //Set force. Use unmodified force here; takedamage applys damage mitigation.
11413 attack.dropv[0] = (float) 3; //Apply drop Y.
11414 attack.dropv[1] = (float) 1.2; //Apply drop X
11415 attack.dropv[2] = (float) 0; //Apply drop Z
11417 if(self->takedamage) //Defender uses takedamage()?
11419 self->takedamage(eOpp, &attack); //Apply attack to kill defender.
11420 } else {
11421 kill(self); //Kill defender instantly.
11423 } else //Total force less then health or using non lethal setting.
11425 if(self->health > iFForce) //Final force less then health?
11427 self->health -= iFForce; //Reduce health directly. Using takedamage() breaks grabs and spams defender's status in HUD.
11428 } else {
11429 self->health = 1; //Set minimum health.
11431 execute_takedamage_script(self, eOpp, iForce, 0, iType, 0, 0, 0, 0); //Execute the takedamage script.
11435 if(iDot == 2 || iDot == 3 || iDot == 5) //MP?
11437 self->mp -= iForce; //Subtract force from MP.
11438 if(self->mp < 0)
11439 self->mp = 0; //Stablize MP at 0.
11446 void adjust_bind(entity * e) {
11447 if(e->bindanim) {
11448 if(e->animnum != e->bound->animnum) {
11449 if(!validanim(e, e->bound->animnum)) {
11450 if(e->bindanim & 4) {
11451 kill(e);
11453 e->bound = NULL;
11454 return;
11456 ent_set_anim(e, e->bound->animnum, 1);
11458 if(e->animpos != e->bound->animpos && e->bindanim & 2) {
11459 update_frame(e, e->bound->animpos);
11462 e->z = e->bound->z + e->bindoffset[1];
11463 e->a = e->bound->a + e->bindoffset[2];
11464 switch (e->bindoffset[3]) {
11465 case 0:
11466 if(e->bound->direction)
11467 e->x = e->bound->x + e->bindoffset[0];
11468 else
11469 e->x = e->bound->x - e->bindoffset[0];
11470 break;
11471 case 1:
11472 e->direction = e->bound->direction;
11473 if(e->bound->direction)
11474 e->x = e->bound->x + e->bindoffset[0];
11475 else
11476 e->x = e->bound->x - e->bindoffset[0];
11477 break;
11478 case -1:
11479 e->direction = !e->bound->direction;
11480 if(e->bound->direction)
11481 e->x = e->bound->x + e->bindoffset[0];
11482 else
11483 e->x = e->bound->x - e->bindoffset[0];
11484 break;
11485 case 2:
11486 e->direction = 1;
11487 e->x = e->bound->x + e->bindoffset[0];
11488 break;
11489 case -2:
11490 e->direction = 0;
11491 e->x = e->bound->x + e->bindoffset[0];
11492 break;
11493 default:
11494 e->x = e->bound->x + e->bindoffset[0];
11495 break;
11496 // the default is no change :), just give a value of 12345 or so
11500 // arrenge the list reduce its length
11501 void arrange_ents() {
11502 int i, ind = -1;
11503 entity *temp;
11504 if(ent_count == 0)
11505 return;
11506 if(ent_max == ent_count) {
11507 for(i = 0; i < ent_max; i++) {
11508 ent_list[i]->update_mark = 0;
11509 if(ent_list[i]->exists && ent_list[i]->bound) {
11510 adjust_bind(ent_list[i]);
11513 } else {
11514 for(i = 0; i < ent_max; i++) {
11515 if(!ent_list[i]->exists && ind < 0)
11516 ind = i;
11517 else if(ent_list[i]->exists && ind >= 0) {
11518 temp = ent_list[i];
11519 ent_list[i] = ent_list[ind];
11520 ent_list[ind] = temp;
11521 ind++;
11523 ent_list[i]->update_mark = 0;
11524 if(ent_list[i]->exists && ent_list[i]->bound) {
11525 adjust_bind(ent_list[i]);
11528 ent_max = ent_count;
11532 // Update all entities that wish to think or animate in this cycle.
11533 // All loops are separated because "self" might die during a pass.
11534 void update_ents() {
11535 int i;
11536 for(i = 0; i < ent_max; i++) {
11537 if(ent_list[i]->exists && borTime != ent_list[i]->timestamp) // dont update fresh entity
11539 self = ent_list[i];
11540 self->update_mark = 0;
11541 if(level)
11542 check_lost(); // check lost caused by level scrolling or lifespan
11543 if(!self->exists)
11544 continue;
11545 // expand time incase being frozen
11546 if(is_frozen(self)) {
11547 expand_time(self);
11548 } else {
11549 execute_updateentity_script(self); // execute a script
11550 if(!self->exists)
11551 continue;
11552 check_ai(); // check ai
11553 if(!self->exists)
11554 continue;
11555 check_gravity(); // check gravity
11556 if(!self->exists)
11557 continue;
11558 update_animation(); // if not frozen, update animation
11559 if(!self->exists)
11560 continue;
11561 check_attack(); // Collission detection
11562 if(!self->exists)
11563 continue;
11564 update_health(); // Update displayed health
11567 } //end of for
11568 arrange_ents();
11572 void display_ents() {
11573 unsigned f;
11574 int i, z, wall = 0, wall2;
11575 entity *e = NULL;
11576 entity *other = NULL;
11577 int qx, qy, sy, sz, alty;
11578 float temp1, temp2;
11579 int useshadow = 0;
11580 int can_mirror = 0;
11581 s_drawmethod *drawmethod = NULL;
11582 s_drawmethod commonmethod;
11583 s_drawmethod shadowmethod;
11584 int use_mirror = (level && level->mirror);
11586 for(i = 0; i < ent_max; i++) {
11587 if(ent_list[i] && ent_list[i]->exists) {
11588 e = ent_list[i];
11589 if(e->modeldata.hpbarstatus.sizex) {
11590 drawenemystatus(e);
11593 if(freezeall || !(e->blink && (borTime % (GAME_SPEED / 10)) < (GAME_SPEED / 20))) { // If special is being executed, display all entities regardless
11594 f = e->animation->sprite[e->animpos];
11596 other = check_platform(e->x, e->z);
11597 wall = checkwall(e->x, e->z);
11599 if(f < sprites_loaded) {
11600 // var "z" takes into account whether it has a setlayer set, whether there are other entities on
11601 // the same "z", in which case there is a layer offset, whether the entity is on an obstacle, and
11602 // whether the entity is grabbing someone and has grabback set
11604 z = (int) e->z; // Set the layer offset
11606 if(e->grabbing && e->modeldata.grabback)
11607 z = (int) e->link->z - 1; // Grab animation displayed behind
11608 else if(!e->modeldata.grabback && e->grabbing)
11609 z = (int) e->link->z + 1;
11611 if(e->bound && e->bound->grabbing) {
11612 if(e->bound->modeldata.grabback)
11613 z--;
11614 else
11615 z++;
11618 if(other && other != e
11619 && e->a >= other->a + other->animation->platform[other->animpos][7]) {
11620 if(e->link
11621 && ((e->modeldata.grabback && !e->grabbing)
11622 || (e->link->modeldata.grabback && e->link->grabbing)
11623 || e->grabbing)
11625 z = (int) (other->z + 2); // Make sure entities get displayed in front of obstacle and grabbee
11627 else
11628 z = (int) (other->z + 1); // Entity should always display in front of the obstacle
11632 if(e->owner)
11633 z = (int) (e->z /* + e->layer */ + 1); // Always in front
11635 if(e->modeldata.setlayer)
11636 z = HOLE_Z + e->modeldata.setlayer; // Setlayer takes precedence
11638 if(checkhole(e->x, e->z) == 2)
11639 z = PANEL_Z - 1; // place behind panels
11641 drawmethod =
11642 e->animation->drawmethods ? getDrawMethod(e->animation, e->animpos) : NULL;
11643 //drawmethod = e->animation->drawmethods?e->animation->drawmethods[e->animpos]:NULL;
11644 if(e->drawmethod.flag)
11645 drawmethod = &(e->drawmethod);
11646 if(!drawmethod)
11647 commonmethod = plainmethod;
11648 else
11649 commonmethod = *drawmethod;
11650 drawmethod = &commonmethod;
11652 if(drawmethod->remap >= 1 && drawmethod->remap <= e->modeldata.maps_loaded) {
11653 drawmethod->table = e->modeldata.colourmap[drawmethod->remap - 1];
11656 if(e->colourmap) {
11657 if(drawmethod->remap < 0)
11658 drawmethod->table = e->colourmap;
11660 if(e->modeldata.alpha >= 1 && e->modeldata.alpha <= MAX_BLENDINGS) {
11661 if(drawmethod->alpha < 0) {
11662 drawmethod->alpha = e->modeldata.alpha;
11665 if(!drawmethod->table)
11666 drawmethod->table = e->modeldata.palette;
11667 if(e->modeldata.globalmap) {
11668 if(level && current_palette)
11669 drawmethod->table = level->palettes[current_palette - 1];
11670 else
11671 drawmethod->table = pal;
11674 if(!e->direction) {
11675 drawmethod->flipx = !drawmethod->flipx;
11676 if(drawmethod->fliprotate && drawmethod->rotate)
11677 drawmethod->rotate = 360 - drawmethod->rotate;
11680 if(!use_mirror || z > MIRROR_Z) // don't display if behind the mirror
11682 spriteq_add_sprite((int) (e->x - (level ? advancex : 0)),
11683 (int) (e->z - e->a + gfx_y_offset), z, f, drawmethod,
11684 e->bound ? e->bound->sortid - 1 : e->sortid);
11687 can_mirror = (use_mirror && self->z > MIRROR_Z);
11688 if(can_mirror) {
11689 spriteq_add_sprite((int) (e->x - (level ? advancex : 0)),
11690 (int) ((2 * MIRROR_Z - e->z) - e->a + gfx_y_offset),
11691 2 * PANEL_Z - z, f, drawmethod,
11692 MAX_ENTS - (e->bound ? e->bound->sortid -
11693 1 : e->sortid));
11695 } //end of if(f<sprites_loaded)
11697 if(e->modeldata.gfxshadow == 1 && f < sprites_loaded) //gfx shadow
11699 useshadow = (e->animation->shadow ? e->animation->shadow[e->animpos] : 1)
11700 && colors.shadow && light[1];
11701 //printf("\n %d, %d, %d\n", shadowcolor, light[0], light[1]);
11702 if(useshadow && e->a >= 0
11703 && (!e->modeldata.aironly || (e->modeldata.aironly && inair(e)))) {
11704 wall = checkwall_below(e->x, e->z, e->a);
11705 if(wall < 0) {
11706 alty = (int) e->a;
11707 temp1 = -1 * e->a * light[0] / 256; // xshift
11708 temp2 = (float) (-alty * light[1] / 256); // zshift
11709 qx = (int) (e->x - advancex /* + temp1 */ );
11710 qy = (int) (e->z + gfx_y_offset /* + temp2 */ );
11711 } else {
11712 alty = (int) (e->a - level->walls[wall].alt);
11713 temp1 = -1 * (e->a - level->walls[wall].alt) * light[0] / 256; // xshift
11714 temp2 = (float) (-alty * light[1] / 256); // zshift
11715 qx = (int) (e->x - advancex /* + temp1 */ );
11716 qy = (int) (e->z + gfx_y_offset /*+ temp2 */ -
11717 level->walls[wall].alt);
11720 wall2 = checkwall_below(e->x + temp1, e->z + temp2, e->a); // check if the shadow drop into a hole or fall on another wall
11721 if(!(checkhole(e->x + temp1, e->z + temp2) && wall2 < 0)) //&& !(wall>=0 && level->walls[wall][7]>e->a))
11723 if(wall >= 0 && wall2 >= 0) {
11724 alty +=
11725 (int) (level->walls[wall].alt -
11726 level->walls[wall2].alt);
11727 /*qx += -1*(level->walls[wall].alt-level->walls[wall2].alt)*light[0]/256;
11728 qy += (level->walls[wall].alt-level->walls[wall2].alt) - (level->walls[wall].alt-level->walls[wall2].alt)*light[1]/256; */
11729 } else if(wall >= 0) {
11730 alty += (int) (level->walls[wall].alt);
11731 /*qx += -1*level->walls[wall].alt*light[0]/256;
11732 qy += level->walls[wall].alt - level->walls[wall].alt*light[1]/256; */
11733 } else if(wall2 >= 0) {
11734 alty -= (int) (level->walls[wall2].alt);
11735 /*qx -= -1*level->walls[wall2].alt * light[0]/256;
11736 qy -= level->walls[wall2].alt - level->walls[wall2][7]*light[1]/256; */
11738 sy = (2 * MIRROR_Z - qy) + 2 * gfx_y_offset;
11739 z = SHADOW_Z;
11740 sz = PANEL_Z - HUD_Z;
11741 if(e->animation->shadow_coords) {
11742 if(e->direction)
11743 qx +=
11744 e->animation->shadow_coords[e->animpos][0];
11745 else
11746 qx -=
11747 e->animation->shadow_coords[e->animpos][0];
11748 qy += e->animation->shadow_coords[e->animpos][1];
11749 sy -= e->animation->shadow_coords[e->animpos][1];
11751 shadowmethod = plainmethod;
11752 shadowmethod.fillcolor = (colors.shadow > 0 ? colors.shadow : 0);
11753 shadowmethod.alpha = shadowalpha;
11754 shadowmethod.scalex = drawmethod->scalex;
11755 shadowmethod.flipx = drawmethod->flipx;
11756 shadowmethod.scaley = light[1] * drawmethod->scaley / 256;
11757 shadowmethod.flipy = drawmethod->flipy;
11758 shadowmethod.centery += alty;
11759 if(shadowmethod.flipy)
11760 shadowmethod.centery = -shadowmethod.centery;
11761 if(shadowmethod.scaley < 0) {
11762 shadowmethod.scaley = -shadowmethod.scaley;
11763 shadowmethod.flipy = !shadowmethod.flipy;
11765 shadowmethod.rotate = drawmethod->rotate;
11766 shadowmethod.shiftx = drawmethod->shiftx + light[0];
11768 spriteq_add_sprite(qx, qy, z, f, &shadowmethod, 0);
11769 if(use_mirror) {
11770 shadowmethod.flipy = !shadowmethod.flipy;
11771 shadowmethod.centery = -shadowmethod.centery;
11772 spriteq_add_sprite(qx, sy, sz, f, &shadowmethod, 0);
11775 } //end of gfxshadow
11776 } else //plan shadow
11778 useshadow =
11779 e->animation->shadow ? e->animation->shadow[e->animpos] : e->modeldata.
11780 shadow;
11781 if(useshadow < 0) {
11782 useshadow = e->modeldata.shadow;
11784 if(useshadow && e->a >= 0
11785 && !(checkhole(e->x, e->z) && checkwall_below(e->x, e->z, e->a) < 0)
11786 && (!e->modeldata.aironly || (e->modeldata.aironly && inair(e)))) {
11787 if(other && other != e
11788 && e->a >=
11789 other->a + other->animation->platform[other->animpos][7]) {
11790 qx = (int) (e->x - advancex);
11791 qy = (int) (e->z - other->a -
11792 other->animation->platform[other->animpos][7] +
11793 gfx_y_offset);
11794 sy = (int) ((2 * MIRROR_Z - e->z) - other->a -
11795 other->animation->platform[other->animpos][7] +
11796 gfx_y_offset);
11797 z = (int) (other->z + 1);
11798 sz = 2 * PANEL_Z - z;
11799 } else if(level && wall >= 0) // && e->a >= level->walls[wall][7])
11801 qx = (int) (e->x - advancex);
11802 qy = (int) (e->z - level->walls[wall].alt + gfx_y_offset);
11803 sy = (int) ((2 * MIRROR_Z - e->z) - level->walls[wall].alt +
11804 gfx_y_offset);
11805 z = SHADOW_Z;
11806 sz = PANEL_Z - HUD_Z;
11807 } else {
11808 qx = (int) (e->x - advancex);
11809 qy = (int) (e->z + gfx_y_offset);
11810 sy = (int) ((2 * MIRROR_Z - e->z) + gfx_y_offset);
11811 z = SHADOW_Z;
11812 sz = PANEL_Z - HUD_Z;
11814 if(e->animation->shadow_coords) {
11815 if(e->direction)
11816 qx += e->animation->shadow_coords[e->animpos][0];
11817 else
11818 qx -= e->animation->shadow_coords[e->animpos][0];
11819 qy += e->animation->shadow_coords[e->animpos][1];
11820 sy -= e->animation->shadow_coords[e->animpos][1];
11822 shadowmethod = plainmethod;
11823 shadowmethod.alpha = BLEND_MULTIPLY + 1;
11824 shadowmethod.flipx = !e->direction;
11825 spriteq_add_sprite(qx, qy, z, shadowsprites[useshadow - 1],
11826 &shadowmethod, 0);
11827 if(use_mirror)
11828 spriteq_add_sprite(qx, sy, sz, shadowsprites[useshadow - 1],
11829 &shadowmethod, 0);
11830 } //end of plan shadow
11832 } // end of blink checking
11834 if(e->arrowon) // Display the players image while invincible to indicate player number
11836 if(e->modeldata.parrow[(int) e->playerindex][0] && e->invincible == 1)
11837 spriteq_add_sprite((int)
11838 (e->x - advancex +
11839 e->modeldata.parrow[(int) e->playerindex][1]),
11840 (int) (e->z - e->a + gfx_y_offset +
11841 e->modeldata.parrow[(int) e->playerindex][2]),
11842 (int) e->z, e->modeldata.parrow[(int) e->playerindex][0],
11843 NULL, e->sortid * 2);
11845 } // end of if(ent_list[i]->exists)
11846 } // end of for
11851 void toss(entity * ent, float lift) {
11852 if(!lift)
11853 return; //zero?
11854 ent->toss_time = borTime + 1;
11855 ent->tossv = lift;
11856 ent->a += 0.5; // Get some altitude (needed for checks)
11861 entity *findent(int types) {
11862 int i;
11863 for(i = 0; i < MAX_ENTS; i++) { // 2007-12-18, remove all nodieblink checking, because dead corpse with nodieblink 3 will be changed to TYPE_NONE
11864 // so if it is "dead" and TYPE_NONE, it must be a corpse
11865 if(ent_list[i]->exists && (ent_list[i]->modeldata.type & types)
11866 && !(ent_list[i]->dead && ent_list[i]->modeldata.type == TYPE_NONE)) {
11867 return ent_list[i];
11870 return NULL;
11875 int count_ents(int types) {
11876 int i;
11877 int count = 0;
11878 for(i = 0; i < MAX_ENTS; i++) { // 2007-12-18, remove all nodieblink checking, because dead corpse with nodieblink 3 will be changed to TYPE_NONE
11879 // so if it is "dead" and TYPE_NONE, it must be a corpse
11880 count += (ent_list[i]->exists && (ent_list[i]->modeldata.type & types)
11881 && !(ent_list[i]->dead && ent_list[i]->modeldata.type == TYPE_NONE));
11883 return count;
11888 entity *find_ent_here(entity * exclude, float x, float z, int types) {
11889 int i;
11890 for(i = 0; i < MAX_ENTS; i++) {
11891 if(ent_list[i]->exists && ent_list[i] != exclude && (ent_list[i]->modeldata.type & types)
11892 && diff(ent_list[i]->x, x) < (self->modeldata.grabdistance * 0.83333)
11893 && diff(ent_list[i]->z, z) < (self->modeldata.grabdistance / 3)
11894 && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]) {
11895 return ent_list[i];
11898 return NULL;
11901 int set_idle(entity * ent) {
11902 //int ani = ANI_IDLE;
11903 //if(validanim(ent,ANI_FAINT) && ent->health <= ent->modeldata.health / 4) ani = ANI_FAINT;
11904 //if(validanim(ent,ani)) ent_set_anim(ent, ani, 0);
11905 if(common_idle_anim(ent)) {
11906 } else
11907 return 0;
11908 ent->idling = 1;
11909 ent->attacking = 0;
11910 ent->inpain = 0;
11911 ent->jumping = 0;
11912 ent->blocking = 0;
11913 return 1;
11916 int set_death(entity * iDie, int type, int reset) {
11917 //iDie->xdir = iDie->zdir = iDie->tossv = 0; // stop the target
11918 if(iDie->blocking && validanim(iDie, ANI_CHIPDEATH)) {
11919 ent_set_anim(iDie, ANI_CHIPDEATH, reset);
11920 iDie->idling = 0;
11921 iDie->getting = 0;
11922 iDie->jumping = 0;
11923 iDie->charging = 0;
11924 iDie->attacking = 0;
11925 iDie->blocking = 0;
11926 return 1;
11928 if(type < 0 || type >= dyn_anim_custom_maxvalues.max_attack_types || !validanim(iDie, dyn_anims.animdies[type]))
11929 type = 0;
11930 if(validanim(iDie, dyn_anims.animdies[type]))
11931 ent_set_anim(iDie, dyn_anims.animdies[type], reset);
11932 else
11933 return 0;
11935 iDie->idling = 0;
11936 iDie->getting = 0;
11937 iDie->jumping = 0;
11938 iDie->charging = 0;
11939 iDie->attacking = 0;
11940 iDie->blocking = 0;
11941 if(iDie->frozen)
11942 unfrozen(iDie);
11943 return 1;
11947 int set_fall(entity * iFall, int type, int reset, entity * other, int force, int drop, int noblock, int guardcost,
11948 int jugglecost, int pauseadd) {
11949 if(type < 0 || type >= dyn_anim_custom_maxvalues.max_attack_types || !validanim(iFall, dyn_anims.animfalls[type]))
11950 type = 0;
11951 if(validanim(iFall, dyn_anims.animfalls[type]))
11952 ent_set_anim(iFall, dyn_anims.animfalls[type], reset);
11953 else
11954 return 0;
11955 iFall->drop = 1;
11956 iFall->inpain = 0;
11957 iFall->idling = 0;
11958 iFall->falling = 1;
11959 iFall->jumping = 0;
11960 iFall->getting = 0;
11961 iFall->charging = 0;
11962 iFall->attacking = 0;
11963 iFall->blocking = 0;
11964 iFall->nograb = 1;
11965 if(iFall->frozen)
11966 unfrozen(iFall);
11967 execute_onfall_script(iFall, other, force, drop, type, noblock, guardcost, jugglecost, pauseadd);
11969 return 1;
11972 int set_rise(entity * iRise, int type, int reset) {
11973 if(type < 0 || type >= dyn_anim_custom_maxvalues.max_attack_types || !validanim(iRise, dyn_anims.animrises[type]))
11974 type = 0;
11975 if(validanim(iRise, dyn_anims.animrises[type]))
11976 ent_set_anim(iRise, dyn_anims.animrises[type], reset);
11977 else
11978 return 0;
11979 iRise->takeaction = common_rise;
11980 // Get up again
11981 iRise->drop = 0;
11982 iRise->falling = 0;
11983 iRise->projectile = 0;
11984 iRise->nograb = 0;
11985 iRise->xdir = self->zdir = self->tossv = 0;
11986 iRise->modeldata.jugglepoints[0] = iRise->modeldata.jugglepoints[1]; //reset jugglepoints
11987 return 1;
11990 int set_riseattack(entity * iRiseattack, int type, int reset) {
11991 if(!validanim(iRiseattack, dyn_anims.animriseattacks[type]) && iRiseattack->modeldata.riseattacktype == 1)
11992 type = 0;
11993 if(iRiseattack->modeldata.riseattacktype == 0 || type < 0 || type >= dyn_anim_custom_maxvalues.max_attack_types)
11994 type = 0;
11995 if(validanim(iRiseattack, dyn_anims.animriseattacks[type]))
11996 ent_set_anim(iRiseattack, dyn_anims.animriseattacks[type], reset);
11997 else
11998 return 0;
11999 self->staydown[2] = 0; //Reset riseattack delay.
12000 set_attacking(iRiseattack);
12001 iRiseattack->drop = 0;
12002 iRiseattack->nograb = 0;
12003 ent_set_anim(iRiseattack, dyn_anims.animriseattacks[type], 0);
12004 iRiseattack->takeaction = common_attack_proc;
12005 iRiseattack->modeldata.jugglepoints[0] = iRiseattack->modeldata.jugglepoints[1]; //reset jugglepoints
12006 return 1;
12009 int set_blockpain(entity * iBlkpain, int type, int reset) {
12010 if(!validanim(iBlkpain, ANI_BLOCKPAIN)) {
12011 iBlkpain->takeaction = common_pain;
12012 return 1;
12014 if(type < 0 || type >= dyn_anim_custom_maxvalues.max_attack_types || !validanim(iBlkpain, dyn_anims.animblkpains[type]))
12015 type = 0;
12016 if(validanim(iBlkpain, dyn_anims.animblkpains[type]))
12017 ent_set_anim(iBlkpain, dyn_anims.animblkpains[type], reset);
12018 else
12019 return 0;
12020 iBlkpain->takeaction = common_block;
12021 set_attacking(iBlkpain);
12022 return 1;
12025 int set_pain(entity * iPain, int type, int reset) {
12026 int pain = 0;
12028 iPain->xdir = iPain->zdir = iPain->tossv = 0; // stop the target
12029 if(iPain->modeldata.guardpoints[1] > 0 && iPain->modeldata.guardpoints[0] <= 0)
12030 pain = ANI_GUARDBREAK;
12031 else if(type == -1 || type >= dyn_anim_custom_maxvalues.max_attack_types)
12032 pain = ANI_GRABBED;
12033 else
12034 pain = dyn_anims.animpains[type];
12035 if(validanim(iPain, pain))
12036 ent_set_anim(iPain, pain, reset);
12037 else if(validanim(iPain, dyn_anims.animpains[0]))
12038 ent_set_anim(iPain, dyn_anims.animpains[0], reset);
12039 else if(validanim(iPain, ANI_IDLE))
12040 ent_set_anim(iPain, ANI_IDLE, reset);
12041 else
12042 return 0;
12044 if(pain == ANI_GRABBED)
12045 iPain->inpain = 0;
12046 else
12047 iPain->inpain = 1;
12049 iPain->idling = 0;
12050 iPain->falling = 0;
12051 iPain->projectile = 0;
12052 iPain->drop = 0;
12053 iPain->attacking = 0;
12054 iPain->getting = 0;
12055 iPain->charging = 0;
12056 iPain->jumping = 0;
12057 iPain->blocking = 0;
12058 if(iPain->modeldata.guardpoints[1] > 0 && iPain->modeldata.guardpoints[0] <= 0)
12059 iPain->modeldata.guardpoints[0] = iPain->modeldata.guardpoints[1];
12060 if(iPain->frozen)
12061 unfrozen(iPain);
12063 execute_onpain_script(iPain, type, reset);
12064 return 1;
12067 //change model, anim_flag 1: reset animation 0: use original animation
12068 void set_model_ex(entity * ent, char *modelname, int index, s_model * newmodel, int anim_flag) {
12069 s_model *model = NULL;
12070 s_model oldmodel;
12071 int animnum, animpos;
12072 int i;
12073 int type = ent->modeldata.type;
12075 model = ent->model;
12076 if(!newmodel) {
12077 if(index >= 0)
12078 newmodel = model_cache[index].model;
12079 else
12080 newmodel = findmodel(modelname);
12082 if(!newmodel)
12083 shutdown(1, "Can't set model for entity '%s', model not found.\n", ent->name);
12084 if(newmodel == model)
12085 return;
12087 animnum = ent->animnum;
12088 animpos = ent->animpos;
12090 if(!(newmodel->model_flag & MODEL_NO_COPY)) {
12091 if(!newmodel->speed)
12092 newmodel->speed = model->speed;
12093 if(!newmodel->runspeed) {
12094 newmodel->runspeed = model->runspeed;
12095 newmodel->runjumpheight = model->runjumpheight;
12096 newmodel->runjumpdist = model->runjumpdist;
12097 newmodel->runupdown = model->runupdown;
12098 newmodel->runhold = model->runhold;
12100 setDestIfDestNeg_int(&newmodel->icon, model->icon);
12101 setDestIfDestNeg_int(&newmodel->iconpain, model->iconpain);
12102 setDestIfDestNeg_int(&newmodel->iconget, model->iconget);
12103 setDestIfDestNeg_int(&newmodel->icondie, model->icondie);
12104 setDestIfDestNeg_int(&newmodel->knife, model->knife);
12105 setDestIfDestNeg_int(&newmodel->pshotno, model->pshotno);
12106 setDestIfDestNeg_int(&newmodel->bomb, model->bomb);
12107 setDestIfDestNeg_int(&newmodel->star, model->star);
12108 setDestIfDestNeg_int(&newmodel->flash, model->flash);
12109 setDestIfDestNeg_int(&newmodel->bflash, model->bflash);
12110 setDestIfDestNeg_int(&newmodel->dust[0], model->dust[0]);
12111 setDestIfDestNeg_int(&newmodel->dust[1], model->dust[1]);
12112 setDestIfDestNeg_int(&newmodel->diesound, model->diesound);
12113 setDestIfDestNeg_char(&newmodel->shadow, model->shadow);
12115 for(i = 0; i < dyn_anim_custom_maxvalues.max_animations; i++) {
12116 if(!newmodel->animation[i] && model->animation[i] && model->animation[i]->numframes > 0)
12117 newmodel->animation[i] = model->animation[i];
12119 // copy the weapon list if model flag is not set to use its own weapon list
12120 if(!(newmodel->model_flag & MODEL_NO_WEAPON_COPY)) {
12121 newmodel->weapnum = model->weapnum;
12122 if(!newmodel->weapon)
12123 newmodel->weapon = model->weapon;
12127 if(anim_flag) {
12128 ent->attacking = 0;
12129 ent_set_model(ent, newmodel->name);
12130 } else {
12131 oldmodel = ent->modeldata;
12132 ent->model = newmodel;
12133 ent->modeldata = *newmodel;
12134 ent->animation = newmodel->animation[ent->animnum];
12135 ent_copy_uninit(ent, &oldmodel);
12138 ent->modeldata.type = type;
12140 copy_all_scripts(&newmodel->scripts, &ent->scripts, 0);
12142 ent_set_colourmap(ent, ent->map);
12145 void set_weapon(entity * ent, int wpnum, int anim_flag) // anim_flag added for scripted midair weapon changing
12147 if(!ent)
12148 return;
12149 //printf("setweapon: %d \n", wpnum);
12151 if(ent->modeldata.weapon && wpnum > 0 && wpnum <= MAX_WEAPONS && (*ent->modeldata.weapon)[wpnum - 1])
12152 set_model_ex(ent, NULL, (*ent->modeldata.weapon)[wpnum - 1], NULL, !anim_flag);
12153 else
12154 set_model_ex(ent, NULL, -1, ent->defaultmodel, 1);
12156 if(ent->modeldata.type == TYPE_PLAYER) // save current weapon for player's weaploss 3
12158 if(ent->modeldata.weaploss[0] >= 3)
12159 player[(int) ent->playerindex].weapnum = wpnum;
12160 else
12161 player[(int) ent->playerindex].weapnum = level->setweap;
12165 //////////////////////////////////////////////////////////////////////////
12166 // common A.I. code for enemies & NPCs
12167 //////////////////////////////////////////////////////////////////////////
12170 entity *melee_find_target() {
12171 return NULL;
12174 entity *long_find_target() {
12175 return NULL;
12178 entity *normal_find_target(int anim) {
12179 int i, min, max;
12180 int index = -1;
12181 min = 0;
12182 max = 9999;
12183 //find the 'nearest' one
12184 for(i = 0; i < ent_max; i++) {
12185 if(ent_list[i]->exists && ent_list[i] != self //cant target self
12186 && (ent_list[i]->modeldata.type & self->modeldata.hostile)
12187 && (anim < 0 || (anim >= 0 && check_range(self, ent_list[i], anim)))
12188 && !ent_list[i]->dead //must be alive
12189 && diff(ent_list[i]->x, self->x) + diff(ent_list[i]->z, self->z) >= min && diff(ent_list[i]->x, self->x) + diff(ent_list[i]->z, self->z) <= max && ent_list[i]->modeldata.stealth[0] <= self->modeldata.stealth[1] //Stealth factor less then perception factor (allows invisibility).
12191 if(index < 0 ||
12192 (index >= 0 &&
12193 (!ent_list[index]->animation->vulnerable[ent_list[index]->animpos] ||
12194 ent_list[index]->invincible == 1
12196 ) || (
12197 (self->x < ent_list[i]->x) == (self->direction) && // don't turn to the one on the back
12198 diff(ent_list[i]->x, self->x) + diff(ent_list[i]->z, self->z)
12199 < diff(ent_list[index]->x, self->x) + diff(ent_list[index]->z, self->z)
12202 index = i;
12205 if(index >= 0) {
12206 return ent_list[index];
12208 return NULL;
12211 int isItem(entity * e) {
12212 return e->modeldata.type & TYPE_ITEM;
12215 int isSubtypeTouch(entity * e) {
12216 return e->modeldata.subtype == SUBTYPE_TOUCH;
12219 int isSubtypeWeapon(entity * e) {
12220 return e->modeldata.subtype == SUBTYPE_WEAPON;
12223 int isSubtypeProjectile(entity * e) {
12224 return e->modeldata.subtype == SUBTYPE_PROJECTILE;
12227 int canBeDamaged(entity * who, entity * bywhom) {
12228 return (who->modeldata.candamage & bywhom->modeldata.type) == bywhom->modeldata.type;
12231 //Used by default A.I. pattern
12232 // A.I. characters try to find a pickable item
12233 entity *normal_find_item() {
12235 int i;
12236 int index = -1;
12237 entity *ce = NULL;
12238 //find the 'nearest' one
12239 for(i = 0; i < ent_max; i++) {
12240 ce = ent_list[i];
12241 if(ce->exists && isItem(ce) &&
12242 diff(ce->x, self->x) + diff(ce->z, self->z) < 300 &&
12243 ce->animation->vulnerable[ce->animpos] &&
12244 (validanim(self, ANI_GET) || (isSubtypeTouch(ce) && canBeDamaged(ce, self))) &&
12245 ((isSubtypeWeapon(ce) && !self->weapent && self->modeldata.weapon
12246 && (*self->modeldata.weapon)[ce->modeldata.weapnum - 1] >= 0)
12247 || (isSubtypeProjectile(ce) && !self->weapent)
12248 || (ce->health && (self->health < self->modeldata.health) && !isSubtypeProjectile(ce)
12249 && !isSubtypeWeapon(ce))
12252 if(index < 0
12253 || diff(ce->x, self->x) + diff(ce->z, self->z) < diff(ent_list[index]->x,
12254 self->x) + diff(ent_list[index]->z,
12255 self->z))
12256 index = i;
12259 if(index >= 0)
12260 return ent_list[index];
12261 return NULL;
12264 int long_attack() {
12265 return 0;
12268 int melee_attack() {
12269 return 0;
12272 // chose next attack in atchain, if succeeded, return 1, otherwise return 0.
12273 int perform_atchain() {
12274 int pickanim = 0;
12275 if(self->combotime > borTime)
12276 self->combostep[0]++;
12277 else
12278 self->combostep[0] = 1;
12280 if(self->modeldata.atchain[self->combostep[0] - 1] == 0) // 0 means the chain ends
12282 self->combostep[0] = 0;
12283 return 0;
12286 if(validanim(self, dyn_anims.animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1])) {
12287 if(((self->combostep[0] == 1 || self->modeldata.combostyle != 1) && self->modeldata.type == TYPE_PLAYER) || // player should use attack 1st step without checking range
12288 (self->modeldata.combostyle != 1 && normal_find_target(dyn_anims.animattacks[self->modeldata.atchain[0] - 1])) || // normal chain just checks the first attack in chain(guess no one like it)
12289 (self->modeldata.combostyle == 1 && normal_find_target(dyn_anims.animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]))) // combostyle 1 checks all anyway
12291 pickanim = 1;
12292 } else if(self->modeldata.combostyle == 1 && self->combostep[0] != 1) // ranged combo? search for a valid attack
12294 while(++self->combostep[0] <= self->modeldata.chainlength) {
12295 if(self->modeldata.atchain[self->combostep[0] - 1] &&
12296 validanim(self, dyn_anims.animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) &&
12297 (self->combostep[0] == self->modeldata.chainlength ||
12298 normal_find_target(dyn_anims.animattacks
12299 [self->modeldata.atchain[self->combostep[0] - 1] - 1]))) {
12300 pickanim = 1;
12301 break;
12305 } else
12306 self->combostep[0] = 0;
12307 if(pickanim) {
12308 ent_set_anim(self, dyn_anims.animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 1);
12309 set_attacking(self);
12310 self->takeaction = common_attack_proc;
12312 if(!pickanim || self->combostep[0] > self->modeldata.chainlength)
12313 self->combostep[0] = 0;
12314 if((self->modeldata.combostyle & 2))
12315 self->combotime = borTime + combodelay;
12316 return pickanim;
12319 void normal_prepare() {
12320 int i, lastpick = -1;
12321 int predir = self->direction;
12322 entity *target = normal_find_target(-1);
12324 self->xdir = self->zdir = 0; //stop
12326 if(!target) {
12327 self->idling = 1;
12328 self->takeaction = NULL;
12329 return;
12331 //check if target is behind, so we can perform a turn back animation
12332 if(!self->modeldata.noflip)
12333 self->direction = (self->x < target->x);
12334 if(predir != self->direction && validanim(self, ANI_TURN))
12336 self->direction = predir;
12337 set_turning(self);
12338 ent_set_anim(self, ANI_TURN, 0);
12339 self->takeaction = common_turn;
12340 return;
12342 // Wait...
12343 if(borTime < self->stalltime)
12344 return;
12345 // let go the projectile, well
12346 if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
12347 validanim(self, ANI_THROWATTACK) && (target = normal_find_target(ANI_THROWATTACK))) {
12348 set_attacking(self);
12349 ent_set_anim(self, ANI_THROWATTACK, 0);
12350 self->takeaction = common_attack_proc;
12351 return;
12353 // move freespecial check here
12354 if((rand32() & 7) < 2) {
12355 for(i = 0; i < dyn_anim_custom_maxvalues.max_freespecials; i++) {
12356 if(validanim(self, dyn_anims.animspecials[i]) &&
12357 (check_energy(1, dyn_anims.animspecials[i]) ||
12358 check_energy(0, dyn_anims.animspecials[i])) &&
12359 (target = normal_find_target(dyn_anims.animspecials[i])) &&
12360 (rand32() % dyn_anim_custom_maxvalues.max_freespecials) < 3 && check_costmove(dyn_anims.animspecials[i], 1)) {
12361 return;
12366 if(self->modeldata.chainlength > 1) // have a chain?
12368 if(perform_atchain())
12369 return;
12370 } else if(target) // dont have a chain so just select an attack randomly
12372 // Pick an attack
12373 for(i = 0; i < dyn_anim_custom_maxvalues.max_attacks; i++) {
12374 if(validanim(self, dyn_anims.animattacks[i]) &&
12375 (target = normal_find_target(dyn_anims.animattacks[i]))) {
12376 lastpick = dyn_anims.animattacks[i];
12377 if((rand32() & 31) > 10)
12378 break;
12381 if(lastpick >= 0) {
12382 set_attacking(self);
12383 ent_set_anim(self, lastpick, 0);
12384 self->takeaction = common_attack_proc;
12385 return;
12388 // No attack to perform, return to A.I. root
12389 self->idling = 1;
12390 self->takeaction = NULL;
12393 void common_jumpland() {
12394 if(self->animating)
12395 return;
12396 set_idle(self);
12397 self->takeaction = NULL;
12400 //A.I characters play the jump animation
12401 void common_jump() {
12402 entity *dust;
12404 if(inair(self)) {
12405 if(self->animation->dive[0] || self->animation->dive[1]) {
12406 self->tossv = 0; // Void tossv so "a" can be adjusted manually
12407 self->toss_time = 0;
12409 if(self->direction)
12410 self->xdir = self->animation->dive[0];
12411 else
12412 self->xdir = -self->animation->dive[0];
12414 self->a -= self->animation->dive[1];
12416 if(self->a <= self->base)
12417 self->a = self->base; // Don't want to go below ground
12419 return;
12422 if(self->tossv <= 0) // wait if it is still go up
12424 self->tossv = 0;
12425 self->a = self->base;
12427 self->jumping = 0;
12428 self->attacking = 0;
12430 if(!self->modeldata.runhold)
12431 self->running = 0;
12433 self->zdir = self->xdir = 0;
12435 if(validanim(self, ANI_JUMPLAND) && self->animation->landframe[0] == -1) // check if jumpland animation exists and not using landframe
12437 ent_set_anim(self, ANI_JUMPLAND, 0);
12438 if(self->modeldata.dust[1] >= 0) {
12439 dust =
12440 spawn(self->x, self->z, self->a, self->direction, NULL, self->modeldata.dust[1],
12441 NULL);
12442 dust->base = self->a;
12443 dust->autokill = 1;
12444 execute_onspawn_script(dust);
12446 self->takeaction = common_jumpland;
12447 } else {
12448 if(self->modeldata.dust[1] >= 0 && self->animation->landframe[0] == -1) {
12449 dust =
12450 spawn(self->x, self->z, self->a, self->direction, NULL, self->modeldata.dust[1],
12451 NULL);
12452 dust->base = self->a;
12453 dust->autokill = 1;
12454 execute_onspawn_script(dust);
12456 if(self->animation->landframe[0] >= 0 && self->animating)
12457 return;
12459 set_idle(self);
12460 self->takeaction = NULL; // back to A.I. root
12465 //A.I. characters spawn
12466 void common_spawn(void) {
12467 self->idling = 0;
12468 if(self->animating)
12469 return;
12470 set_idle(self);
12471 self->takeaction = NULL; // come to life
12474 //A.I. characters drop from the sky
12475 void common_drop(void) {
12476 if(inair(self))
12477 return;
12478 self->idling = 1;
12479 self->takeaction = NULL;
12480 if(self->health <= 0)
12481 kill(self);
12484 //walk off a wall/cliff
12485 void common_walkoff(void) {
12486 if(inair(self) || self->animating)
12487 return;
12488 self->takeaction = NULL;
12489 set_idle(self);
12492 // play turn animation and then flip
12493 void common_turn(void) {
12494 if(!self->animating) {
12495 self->xdir = self->zdir = 0;
12496 self->direction = !self->direction;
12497 set_idle(self);
12498 self->takeaction = NULL;
12502 // switch to land animation, land safely
12503 void doland(void) {
12504 self->xdir = self->zdir = 0;
12505 self->drop = 0;
12506 self->projectile = 0;
12507 self->damage_on_landing = 0;
12508 if(validanim(self, ANI_LAND)) {
12509 self->direction = !self->direction;
12510 ent_set_anim(self, ANI_LAND, 0);
12511 self->takeaction = common_land;
12512 } else {
12513 set_idle(self);
12514 self->takeaction = NULL;
12518 void common_fall(void) {
12519 // Still falling?
12520 if(self->falling || inair(self) || self->tossv) {
12521 return;
12524 //self->xdir = self->zdir;
12526 // Landed
12527 if(self->projectile > 0) {
12528 if(self->projectile == 2) { // damage_on_landing==-2 means a player has pressed up+jump and has a land animation
12529 if((autoland == 1 && self->damage_on_landing == -1) || self->damage_on_landing == -2) {
12530 // Added autoland option for landing
12531 doland();
12532 return;
12535 //self->projectile = 0;
12536 self->falling = 0;
12538 // Drop Weapon due to Enemy Falling.
12539 if(self->modeldata.weaploss[0] == 1)
12540 dropweapon(1);
12542 if(self->boss && level_completed)
12543 tospeedup = 1;
12545 // Pause a bit...
12546 self->takeaction = common_lie;
12547 self->stalltime = self->staydown[0] + (borTime + GAME_SPEED - self->modeldata.risetime[0]); //Set rise delay.
12548 self->staydown[2] = self->staydown[1] + (borTime - self->modeldata.risetime[1]); //Set rise attack delay.
12549 self->staydown[0] = 0; //Reset staydown.
12550 self->staydown[1] = 0; //Reset staydown atk.
12553 void common_try_riseattack(void) {
12554 entity *target;
12555 if(!validanim(self, ANI_RISEATTACK))
12556 return;
12558 target = normal_find_target(ANI_RISEATTACK);
12559 if(!target) {
12560 self->direction = !self->direction;
12561 target = normal_find_target(ANI_RISEATTACK);
12562 self->direction = !self->direction;
12565 if(target) {
12566 self->direction = (target->x > self->x); // Stands up and swings in the right direction depending on chosen target
12567 set_riseattack(self, self->damagetype, 0);
12571 void common_lie(void) {
12572 // Died?
12573 if(self->health <= 0) {
12574 // Drop Weapon due to death.
12575 if(self->modeldata.weaploss[0] <= 2)
12576 dropweapon(1);
12577 if(self->modeldata.falldie == 2)
12578 set_death(self, self->damagetype, 0);
12579 if(!self->modeldata.nodieblink || (self->modeldata.nodieblink == 1 && !self->animating)) { // Now have the option to blink or not
12580 self->takeaction = (self->modeldata.type == TYPE_PLAYER) ? player_blink : suicide;
12581 self->blink = 1;
12582 self->stalltime = self->nextthink = borTime + GAME_SPEED * 2;
12583 } else if(self->modeldata.nodieblink == 2 && !self->animating) {
12584 self->takeaction = (self->modeldata.type == TYPE_PLAYER) ? player_die : suicide;
12586 } else if(self->modeldata.nodieblink == 3 && !self->animating) {
12587 if(self->modeldata.type == TYPE_PLAYER) {
12588 self->takeaction = player_die;
12590 } else {
12591 self->modeldata.type = TYPE_NONE;
12592 self->noaicontrol = 1;
12596 if(self->modeldata.komap[0]) //Have a KO map?
12598 if(self->modeldata.komap[1]) //Wait for fall/death animation to finish?
12600 if(!self->animating) {
12601 self->colourmap = self->modeldata.colourmap[self->modeldata.komap[0] - 1]; //If finished animating, apply map.
12603 } else //Don't bother waiting.
12605 self->colourmap = self->modeldata.colourmap[self->modeldata.komap[0] - 1]; //Apply map.
12609 return;
12612 if(borTime < self->stalltime || self->a != self->base || self->tossv)
12613 return;
12615 //self->takeaction = common_rise;
12616 // Get up again
12617 //self->drop = 0;
12618 //self->falling = 0;
12619 //self->projectile = 0;
12620 //self->xdir = self->zdir = self->tossv = 0;
12622 set_rise(self, self->damagetype, 0);
12625 // rise proc
12626 void common_rise(void) {
12627 if(self->animating)
12628 return;
12629 self->staydown[2] = 0; //Reset riseattack delay.
12630 set_idle(self);
12631 if(self->modeldata.riseinv) {
12632 self->blink = self->modeldata.riseinv > 0;
12633 self->invinctime = borTime + ABS(self->modeldata.riseinv);
12634 self->invincible = 1;
12636 self->takeaction = NULL;
12639 // pain proc
12640 void common_pain(void) {
12641 //self->xdir = self->zdir = 0; // complained
12643 if(self->animating || inair(self))
12644 return;
12646 self->inpain = 0;
12647 if(self->link) {
12648 // set_pain(self, -1, 0);
12649 self->takeaction = common_grabbed;
12650 } else if(self->blocking) {
12651 ent_set_anim(self, ANI_BLOCK, 1);
12652 self->takeaction = common_block;
12653 } else {
12654 set_idle(self);
12655 self->takeaction = NULL;
12659 void doprethrow(void) {
12660 entity *other = self->link;
12661 self->xdir = self->zdir = self->tossv = other->xdir = other->zdir = other->tossv = 0;
12662 ent_set_anim(self, ANI_THROW, 0);
12663 other->takeaction = common_prethrow;
12664 self->takeaction = common_throw_wait;
12667 // 1 grabattack 2 grabforward 3 grabup 4 grabdown 5 grabbackward
12668 // other means grab finisher at once
12669 void dograbattack(int which) {
12670 entity *other = self->link;
12671 other->xdir = other->zdir = self->xdir = self->zdir = 0;
12672 if(which < 5 && which >= 0) {
12673 ++self->combostep[which];
12674 if(self->combostep[which] < 3)
12675 ent_set_anim(self, grab_attacks[which][0], 0);
12676 else {
12677 if(validanim(self, grab_attacks[which][1]))
12678 ent_set_anim(self, grab_attacks[which][1], 0);
12679 else
12680 ent_set_anim(self, ANI_ATTACK3, 0);
12681 memset(self->combostep, 0, sizeof(int) * 5);
12683 } else {
12684 if(validanim(self, grab_attacks[0][1]))
12685 ent_set_anim(self, grab_attacks[0][1], 0);
12686 else if(validanim(self, ANI_ATTACK3))
12687 ent_set_anim(self, ANI_ATTACK3, 0);
12688 else
12689 return;
12690 memset(self->combostep, 0, sizeof(int) * 5);
12692 self->attacking = 1;
12693 self->takeaction = common_grabattack;
12696 void dovault(void) {
12697 int heightvar;
12698 entity *other = self->link;
12699 self->link->xdir = self->link->zdir = self->xdir = self->zdir = 0;
12701 self->attacking = 1;
12702 self->x = other->x;
12704 if(other->animation->height)
12705 heightvar = other->animation->height;
12706 else
12707 heightvar = other->modeldata.height;
12709 self->base = other->base + heightvar;
12710 ent_set_anim(self, ANI_VAULT, 0);
12711 self->takeaction = common_vault;
12714 void common_grab_check(void) {
12715 int rnum, which;
12716 entity *other = self->link;
12718 if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
12719 return;
12721 if(self->base != other->base) { // Change this from ->a to ->base
12722 ent_unlink(self);
12723 set_idle(self);
12724 self->takeaction = NULL;
12725 return;
12728 if(!nolost && self->modeldata.weaploss[0] <= 0)
12729 dropweapon(1);
12731 self->attacking = 0; //for checking
12733 rnum = rand32() & 31;
12735 if(borTime > self->releasetime) {
12736 if(rnum < 12) {
12737 // Release
12738 ent_unlink(self);
12739 set_idle(self);
12740 self->takeaction = NULL;
12741 return;
12742 } else
12743 self->releasetime = borTime + (GAME_SPEED / 2);
12746 if(validanim(self, ANI_THROW) && rnum < 7) {
12747 if(self->modeldata.throwframewait >= 0)
12748 doprethrow();
12749 else
12750 dothrow();
12751 return;
12753 //grab finisher
12754 if(rnum < 4) {
12755 dograbattack(-1);
12756 return;
12758 which = rnum % 5;
12759 // grab attacks
12760 if(rnum > 12 && validanim(self, grab_attacks[which][0])) {
12761 dograbattack(which);
12762 return;
12764 // Vaulting.
12765 if(rnum < 8 && validanim(self, ANI_VAULT)) {
12766 dovault();
12767 return;
12771 //grabbing someone
12772 void common_grab(void) {
12773 // if(self->link) return;
12774 if(self->link || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
12775 return;
12777 memset(self->combostep, 0, sizeof(int) * 5);
12778 set_idle(self);
12779 self->takeaction = NULL;
12780 self->attacking = 0;
12783 // being grabbed
12784 void common_grabbed(void) {
12785 // Just check if we're still grabbed...
12786 if(self->link)
12787 return;
12789 set_idle(self);
12790 self->stalltime = 0;
12791 self->takeaction = NULL;
12794 // picking up something
12795 void common_get(void) {
12796 if(self->animating)
12797 return;
12799 set_idle(self);
12800 self->getting = 0;
12801 self->takeaction = NULL;
12804 // A.I. characters do the block
12805 void common_block(void) {
12806 if(self->animating)
12807 return;
12809 set_idle(self);
12810 self->blocking = 0;
12811 self->takeaction = NULL;
12815 void common_charge(void) {
12816 if(self->animating)
12817 return;
12819 set_idle(self);
12820 self->charging = 0;
12821 self->takeaction = NULL;
12825 // common code for entities hold an item
12826 entity *drop_item(entity * e) {
12827 s_spawn_entry p;
12828 entity *item;
12829 memset(&p, 0, sizeof(s_spawn_entry));
12831 p.index = e->item;
12832 p.itemindex = p.weaponindex = -1;
12833 strcpy(p.alias, e->itemalias);
12834 p.a = e->a + 0.01; // for check, or an enemy "item" will drop from the sky
12835 p.health[0] = e->itemhealth;
12836 p.alpha = e->itemtrans;
12837 p.colourmap = e->itemmap;
12838 p.flip = e->direction;
12840 item = smartspawn(&p);
12842 if(item) {
12843 item->x = e->x;
12844 item->z = e->z;
12845 if(item->x < advancex)
12846 item->x = advancex + 10;
12847 else if(item->x > advancex + videomodes.hRes)
12848 item->x = advancex + videomodes.hRes - 10;
12849 if(!(level->scrolldir & (SCROLL_UP | SCROLL_DOWN))) {
12850 if(item->z - item->a < advancey)
12851 item->z = advancey + 10;
12852 else if(item->z - item->a > advancey + videomodes.vRes)
12853 item->z = advancey + videomodes.vRes - 10;
12855 if(e->boss && item->modeldata.type == TYPE_ENEMY)
12856 item->boss = 1;
12858 return item;
12861 //drop the driver, just spawn, dont takedamage
12862 // damage will adjust by the biker
12863 entity *drop_driver(entity * e) {
12864 int i;
12865 s_spawn_entry p;
12866 entity *driver;
12867 memset(&p, 0, sizeof(s_spawn_entry));
12869 if(e->modeldata.rider >= 0)
12870 p.index = e->modeldata.rider;
12871 else
12872 return NULL; // should not happen, just in case
12873 /*p.x = e->x - advancex; p.z = e->z; */ p.a = e->a + 10;
12874 p.itemindex = e->item;
12875 p.weaponindex = -1;
12876 strcpy(p.itemalias, e->itemalias);
12877 strcpy(p.alias, e->name);
12878 p.itemmap = e->itemmap;
12879 p.itemtrans = e->itemtrans;
12880 p.itemhealth = e->itemhealth;
12881 p.itemplayer_count = e->itemplayer_count;
12882 //p.colourmap = e->map;
12883 for(i = 0; i < MAX_PLAYERS; i++)
12884 p.health[i] = e->modeldata.health;
12885 p.boss = e->boss;
12887 driver = smartspawn(&p);
12888 if(driver) {
12889 driver->x = e->x;
12890 driver->z = e->z;
12892 return driver;
12896 void checkdeath(void) {
12897 entity *item;
12898 if(self->health > 0)
12899 return;
12900 self->dead = 1;
12901 //be careful, since the opponent can be other types
12902 if(self->opponent && self->opponent->modeldata.type == TYPE_PLAYER) {
12903 addscore(self->opponent->playerindex, self->modeldata.score); // Add score to the player
12905 self->nograb = 1;
12906 self->idling = 0;
12908 sound_play_sample(self->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100);
12910 // drop item
12911 if(self->item && count_ents(TYPE_PLAYER) > self->itemplayer_count) { //4player
12912 item = drop_item(self);
12915 if(self->boss) {
12916 self->boss = 0;
12917 --level->bosses;
12918 if(!level->bosses && self->modeldata.type == TYPE_ENEMY) {
12919 kill_all_enemies();
12920 level_completed = 1;
12925 void checkdamageflip(entity * other, s_attack * attack) {
12926 if(other == NULL || other == self
12927 || (!self->drop
12928 && (attack->no_pain || self->modeldata.nopain
12929 || (self->modeldata.defense_pain[(short) attack->attack_type]
12930 && attack->attack_force < self->modeldata.defense_pain[(short) attack->attack_type]))))
12931 return;
12933 if(!self->frozen && !self->modeldata.noflip) // && !inair(self))
12935 if(attack->force_direction == 0) {
12936 if(self->x < other->x)
12937 self->direction = 1;
12938 else if(self->x > other->x)
12939 self->direction = 0;
12940 } else if(attack->force_direction == 1) {
12941 self->direction = other->direction;
12942 } else if(attack->force_direction == -1) {
12943 self->direction = !other->direction;
12944 } else if(attack->force_direction == 2) {
12945 self->direction = 1;
12946 } else if(attack->force_direction == -2) {
12947 self->direction = 0;
12952 void checkdamageeffects(s_attack * attack) {
12953 #define _freeze attack->freeze
12954 #define _maptime attack->maptime
12955 #define _freezetime attack->freezetime
12956 #define _remap attack->forcemap
12957 #define _blast attack->blast
12958 #define _steal attack->steal
12959 #define _seal attack->seal
12960 #define _sealtime attack->sealtime
12961 #define _dot attack->dot
12962 #define _dot_index attack->dot_index
12963 #define _dot_time attack->dot_time
12964 #define _dot_force attack->dot_force
12965 #define _dot_rate attack->dot_rate
12966 #define _staydown0 attack->staydown[0]
12967 #define _staydown1 attack->staydown[1]
12969 entity *opp = self->opponent;
12971 if(_steal && opp && opp != self) {
12972 if(self->health >= attack->attack_force)
12973 opp->health += attack->attack_force;
12974 else
12975 opp->health += self->health;
12976 if(opp->health > opp->modeldata.health)
12977 opp->health = opp->modeldata.health;
12979 if(_freeze && !self->frozen && !self->owner && !self->modeldata.nomove) { // New freeze attack - If not frozen, freeze entity unless it's a projectile
12980 self->frozen = 1;
12981 if(self->freezetime == 0)
12982 self->freezetime = borTime + _freezetime;
12983 if(_remap == -1 && self->modeldata.fmap != -1)
12984 self->colourmap = self->modeldata.colourmap[self->modeldata.fmap - 1]; //12/14/2007 Damon Caskey: If opponents fmap = -1 or only stun, then don't change the color map.
12985 self->drop = 0;
12986 } else if(self->frozen) {
12987 unfrozen(self);
12988 self->drop = 1;
12991 if(_remap > 0 && !_freeze) {
12992 self->maptime = borTime + _maptime;
12993 self->colourmap = self->modeldata.colourmap[_remap - 1];
12996 if(_seal) //Sealed: Disable special moves.
12998 self->sealtime = borTime + _sealtime; //Set time to apply seal. No specials for you!
12999 self->seal = _seal; //Set seal. Any animation with energycost > seal is disabled.
13002 if(_dot) //dot: Damage over time effect.
13004 self->dot_owner[_dot_index] = self->opponent; //dot owner.
13005 self->dot[_dot_index] = _dot; //Mode: 1. HP (non lethal), 2. MP, 3. HP (non lethal) & MP, 4. HP, 5. HP & MP.
13006 self->dot_time[_dot_index] = borTime + (_dot_time * GAME_SPEED / 100); //Gametime dot will expire.
13007 self->dot_force[_dot_index] = _dot_force; //How much to dot each tick.
13008 self->dot_rate[_dot_index] = _dot_rate; //Delay between dot ticks.
13009 self->dot_atk[_dot_index] = attack->attack_type; //dot attack type.
13013 if(self->modeldata.nodrop)
13014 self->drop = 0; // Static enemies/nodrop enemies cannot be knocked down
13016 if(inair(self) && !self->frozen && self->modeldata.nodrop < 2)
13017 self->drop = 1;
13019 if(attack->no_pain)
13020 self->drop = 0;
13022 self->projectile = _blast;
13024 if(self->drop) {
13025 self->staydown[0] = _staydown0; //Staydown: Add to risetime until next rise.
13026 self->staydown[1] = _staydown1;
13028 #undef _freeze
13029 #undef _maptime
13030 #undef _freezetime
13031 #undef _remap
13032 #undef _blast
13033 #undef _steal
13034 #undef _seal
13035 #undef _sealtime
13036 #undef _dot
13037 #undef _dot_index
13038 #undef _dot_time
13039 #undef _dot_force
13040 #undef _dot_rate
13041 #undef _staydown0
13042 #undef _staydown1
13045 void checkdamagedrop(s_attack * attack) {
13046 int attackdrop = attack->attack_drop;
13047 float fdefense_knockdown = self->modeldata.defense_knockdown[(short) attack->attack_type];
13048 if(self->modeldata.animal)
13049 self->drop = 1;
13050 if(self->modeldata.guardpoints[1] > 0 && self->modeldata.guardpoints[0] <= 0)
13051 attackdrop = 0; //guardbreak does not knock down.
13052 if(self->drop || attack->no_pain)
13053 return; // just in case, if we already fall, dont check fall again
13054 // reset count if knockdowntime expired.
13055 if(self->knockdowntime && self->knockdowntime < borTime)
13056 self->knockdowncount = self->modeldata.knockdowncount;
13058 self->knockdowncount -= (attackdrop * fdefense_knockdown);
13059 self->knockdowntime = borTime + GAME_SPEED;
13060 self->drop = (self->knockdowncount < 0); // knockdowncount < 0 means knocked down
13063 void checkmpadd() {
13064 entity *other = self->opponent;
13065 if(other == NULL || other == self)
13066 return;
13068 if(magic_type == 1) {
13069 if(other->modeldata.mprate)
13070 other->mp += other->modeldata.mprate;
13071 else
13072 other->mp++;
13074 if(other->mp > other->modeldata.mp)
13075 other->mp = other->modeldata.mp;
13079 void checkhitscore(entity * other, s_attack * attack) {
13080 entity *opp = self->opponent;
13081 if(!opp)
13082 return;
13083 if(opp && opp != self && opp->modeldata.type == TYPE_PLAYER) { // Added obstacle so explosions can hurt enemies
13084 addscore(opp->playerindex, attack->attack_force * self->modeldata.multiple); // New multiple variable
13085 control_rumble(opp->playerindex, attack->attack_force * 2);
13087 // Don't animate or fall if hurt by self, since
13088 // it means self fell to the ground already. :)
13089 // Add throw score to the player
13090 else if(other == self && self->damage_on_landing > 0)
13091 addscore(opp->playerindex, attack->attack_force);
13094 void checkdamage(entity * other, s_attack * attack) {
13095 int force = attack->attack_force;
13096 int type = attack->attack_type;
13097 if(self->modeldata.guardpoints[1] > 0 && self->modeldata.guardpoints[0] <= 0)
13098 force = 0; //guardbreak does not deal damage.
13099 if(!(self->damage_on_landing && self == other) && !other->projectile && type >= 0 && type < dyn_anim_custom_maxvalues.max_attack_types) {
13100 force = (int) (force * other->modeldata.offense_factors[type]);
13101 force = (int) (force * self->modeldata.defense_factors[type]);
13104 self->health -= force; //Apply damage.
13106 if(self->health > self->modeldata.health)
13107 self->health = self->modeldata.health; //Cap negative damage to max health.
13109 execute_takedamage_script(self, other, force, attack->attack_drop, type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add); //Execute the take damage script.
13111 if(self->health <= 0) //Health at 0?
13113 if(!(self->a < PIT_DEPTH || self->lifespancountdown < 0)) //Not a pit death or countdown?
13115 if(self->invincible == 2) //Invincible type 2?
13117 self->health = 1; //Stop at 1hp.
13118 } else if(self->invincible == 3) //Invincible type 3?
13120 self->health = self->modeldata.health; //Reset to max health.
13123 execute_ondeath_script(self, other, force, attack->attack_drop, type, attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add); //Execute ondeath script.
13127 int checkgrab(entity * other, s_attack * attack) {
13128 //if(attack->no_pain) return 0; //no effect, let modders to deside, don't bother check it here
13129 if(self != other && attack->grab && cangrab(other, self)) {
13130 if(adjust_grabposition(other, self, attack->grab_distance, attack->grab)) {
13131 ents_link(other, self);
13132 self->a = other->a;
13133 } else
13134 return 0;
13136 return 1;
13139 int arrow_takedamage(entity * other, s_attack * attack) {
13140 self->modeldata.no_adjust_base = 0;
13141 self->modeldata.subject_to_wall = self->modeldata.subject_to_platform = self->modeldata.subject_to_hole =
13142 self->modeldata.subject_to_gravity = 1;
13143 if(common_takedamage(other, attack) && self->dead) {
13144 return 1;
13146 return 0;
13149 int common_takedamage(entity * other, s_attack * attack) {
13150 if(self->dead)
13151 return 0;
13152 if(self->toexplode == 2)
13153 return 0;
13154 // fake 'grab', if failed, return as the attack hit nothing
13155 if(!checkgrab(other, attack))
13156 return 0; // try to grab but failed, so return 0 means attack missed
13158 // set pain_time so it wont get hit too often
13159 self->pain_time = borTime + (GAME_SPEED / 5);
13160 // set oppoent
13161 if(self != other)
13162 set_opponent(self, other);
13163 // adjust type
13164 if(attack->attack_type >= 0 && attack->attack_type < dyn_anim_custom_maxvalues.max_attack_types)
13165 self->damagetype = attack->attack_type;
13166 else
13167 self->damagetype = ATK_NORMAL;
13168 // pre-check drop
13169 checkdamagedrop(attack);
13170 // Drop Weapon due to being hit.
13171 if(self->modeldata.weaploss[0] <= 0)
13172 dropweapon(1);
13173 // check effects, e.g., frozen, blast, steal
13174 if(!(self->modeldata.guardpoints[1] > 0 && self->modeldata.guardpoints[0] <= 0))
13175 checkdamageeffects(attack);
13176 // check flip direction
13177 checkdamageflip(other, attack);
13178 // mprate can also control the MP recovered per hit.
13179 checkmpadd();
13180 //damage score
13181 checkhitscore(other, attack);
13182 // check damage, cost hp.
13183 checkdamage(other, attack);
13184 // is it dead now?
13185 checkdeath();
13187 if(self->modeldata.type == TYPE_PLAYER)
13188 control_rumble(self->playerindex, attack->attack_force * 3);
13189 if(self->a <= PIT_DEPTH && self->dead) {
13190 if(self->modeldata.type == TYPE_PLAYER)
13191 player_die();
13192 else
13193 kill(self);
13194 return 1;
13196 // fall to the ground so dont fall again
13197 if(self->damage_on_landing) {
13198 self->damage_on_landing = 0;
13199 return 1;
13201 // unlink due to being hit
13202 if((self->opponent && self->opponent->grabbing != self) || self->dead || self->frozen || self->drop) {
13203 ent_unlink(self);
13205 // Enemies can now use SPECIAL2 to escape cheap attack strings!
13206 if(self->modeldata.escapehits) {
13207 if(self->drop)
13208 self->escapecount = 0;
13209 else
13210 self->escapecount++;
13212 // New pain, fall, and death animations. Also, the nopain flag.
13213 if(self->drop || self->health <= 0) {
13214 // Drop Weapon due to death.
13215 if(self->modeldata.weaploss[0] <= 2)
13216 dropweapon(1);
13217 if(self->health <= 0 && self->modeldata.falldie == 1) {
13218 self->xdir = self->zdir = self->tossv = 0;
13219 set_death(self, self->damagetype, 0);
13220 } else {
13221 self->xdir = attack->dropv[1];
13222 self->zdir = attack->dropv[2];
13223 if(self->direction)
13224 self->xdir = -self->xdir;
13225 toss(self, attack->dropv[0]);
13226 self->damage_on_landing = attack->damage_on_landing;
13227 self->knockdowncount = self->modeldata.knockdowncount; // reset the knockdowncount
13228 self->knockdowntime = 0;
13230 // Now if no fall/die animations exist, entity simply disapears
13231 //set_fall(entity *iFall, int type, int reset, entity* other, int force, int drop)
13232 if(!set_fall
13233 (self, self->damagetype, 1, other, attack->attack_force, attack->attack_drop,
13234 attack->no_block, attack->guardcost, attack->jugglecost, attack->pause_add)) {
13235 if(self->modeldata.type == TYPE_PLAYER)
13236 player_die();
13237 else
13238 kill(self);
13239 return 1;
13242 self->takeaction = common_fall;
13243 if(self->modeldata.type == TYPE_PLAYER)
13244 control_rumble(self->playerindex, attack->attack_force * 3);
13245 } else if(attack->grab && !attack->no_pain) {
13246 set_pain(self, self->damagetype, 0);
13247 other->stalltime = borTime + GRAB_STALL;
13248 self->releasetime = borTime + (GAME_SPEED / 2);
13249 self->takeaction = common_pain;
13250 other->takeaction = common_grabattack;
13252 // Don't change to pain animation if frozen
13253 else if(!self->frozen && !self->modeldata.nopain && !attack->no_pain
13254 && !(self->modeldata.defense_pain[(short) attack->attack_type]
13255 && attack->attack_force < self->modeldata.defense_pain[(short) attack->attack_type])) {
13256 set_pain(self, self->damagetype, 1);
13257 self->takeaction = common_pain;
13259 return 1;
13262 // A.I. try upper cut
13263 int common_try_upper(entity * target) {
13264 if(!validanim(self, ANI_UPPER))
13265 return 0;
13268 if(!target)
13269 target = normal_find_target(ANI_UPPER);
13271 // Target jumping? Try uppercut!
13272 if(target && target->jumping) {
13273 set_attacking(self);
13274 self->zdir = self->xdir = 0;
13275 // Don't waste any time!
13276 ent_set_anim(self, ANI_UPPER, 0);
13277 self->takeaction = common_attack_proc;
13278 return 1;
13280 return 0;
13283 int common_try_runattack(entity * target) {
13284 if(!self->running || !validanim(self, ANI_RUNATTACK))
13285 return 0;
13288 if(!target)
13289 target = normal_find_target(ANI_RUNATTACK);
13291 if(target) {
13292 self->zdir = self->xdir = 0;
13293 set_attacking(self);
13294 ent_set_anim(self, ANI_RUNATTACK, 0);
13295 self->takeaction = common_attack_proc;
13296 return 1;
13298 return 0;
13301 int common_try_block(entity * target) {
13302 if(self->modeldata.nopassiveblock == 0 ||
13303 (rand32() & self->modeldata.blockodds) != 1 || !validanim(self, ANI_BLOCK))
13304 return 0;
13306 if(!target)
13307 target = normal_find_target(ANI_BLOCK);
13309 // no passive block, so block by himself :)
13310 if(target && target->attacking) {
13311 set_blocking(self);
13312 self->zdir = self->xdir = 0;
13313 ent_set_anim(self, ANI_BLOCK, 0);
13314 self->takeaction = common_block;
13315 return 1;
13317 return 0;
13321 int common_try_freespecial(entity* target)
13323 int i, s=dyn_anim_custom_maxvalues.max_freespecials;
13325 for(i=0; i<s; i++)
13327 if(validanim(self,animspecials[i]) &&
13328 (check_energy(1, animspecials[i]) ||
13329 check_energy(0, animspecials[i])) &&
13330 (target || (target=normal_find_target(animspecials[i]))) &&
13331 (rand32()%s)<3 &&
13332 check_costmove(animspecials[i], 1) )
13334 return 1;
13338 return 0;
13341 int common_try_normalattack(entity * target) {
13342 int i, found = 0;
13344 for(i = 0; !found && i < dyn_anim_custom_maxvalues.max_freespecials; i++) {
13345 if(validanim(self, dyn_anims.animspecials[i]) &&
13346 (check_energy(1, dyn_anims.animspecials[i]) ||
13347 check_energy(0, dyn_anims.animspecials[i])) &&
13348 (target || (target = normal_find_target(dyn_anims.animspecials[i]))) && (rand32() % dyn_anim_custom_maxvalues.max_freespecials) < 3) {
13349 found = 1;
13353 for(i = 0; !found && i < dyn_anim_custom_maxvalues.max_attacks; i++) // TODO: recheck range for attacks chains
13355 if(!validanim(self, dyn_anims.animattacks[i]))
13356 continue;
13358 if(target || (target = normal_find_target(dyn_anims.animattacks[i]))) {
13359 found = 1;
13363 if(!found && validanim(self, ANI_THROWATTACK) &&
13364 self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE &&
13365 (target || (target = normal_find_target(ANI_THROWATTACK)))) {
13366 found = 1;
13369 if(found) {
13370 self->zdir = self->xdir = 0;
13371 set_idle(self);
13372 self->idling = 0; // not really idle, in fact it is thinking
13373 self->attacking = -1; // pre-attack, for AI-block check
13374 self->takeaction = normal_prepare;
13375 if(self->combostep[0] && self->combotime > borTime)
13376 self->stalltime = borTime + 1;
13377 else
13378 self->stalltime =
13379 borTime + (GAME_SPEED / 4) + (rand32() % (GAME_SPEED / 10) - self->modeldata.aggression);
13380 return 1;
13383 return 0;
13386 int common_try_jumpattack(entity * target) {
13387 entity *dust;
13388 int rnum;
13390 if((validanim(self, ANI_JUMPATTACK) || validanim(self, ANI_JUMPATTACK2))) {
13391 if(!validanim(self, ANI_JUMPATTACK))
13392 rnum = 1;
13393 else if(validanim(self, ANI_JUMPATTACK2) && (rand32() & 1))
13394 rnum = 1;
13395 else
13396 rnum = 0;
13398 if(rnum == 0 &&
13399 // do a jumpattack
13400 (target || (target = normal_find_target(ANI_JUMPATTACK)))) {
13401 ent_set_anim(self, ANI_JUMPATTACK, 0);
13402 if(self->direction)
13403 self->xdir = (float) 1.3;
13404 else
13405 self->xdir = (float) -1.3;
13406 self->zdir = 0;
13407 } else if(rnum == 1 &&
13408 // do a jumpattack2
13409 (target || (target = normal_find_target(ANI_JUMPATTACK2)))) {
13410 ent_set_anim(self, ANI_JUMPATTACK2, 0);
13411 self->xdir = self->zdir = 0;
13412 } else {
13413 rnum = -1;
13416 if(rnum >= 0) {
13418 set_attacking(self);
13419 self->jumping = 1;
13420 toss(self, self->modeldata.jumpheight);
13421 self->takeaction = common_jump;
13423 if(self->modeldata.dust[2] >= 0) {
13424 dust =
13425 spawn(self->x, self->z, self->a, self->direction, NULL, self->modeldata.dust[2],
13426 NULL);
13427 dust->base = self->a;
13428 dust->autokill = 1;
13429 execute_onspawn_script(dust);
13432 return 1;
13435 return 0;
13438 // Normal attack style
13439 // Used by root A.I., what to do if a target is found.
13440 // return 0 if no action is token
13441 // return 1 if an action is token
13442 int normal_attack() {
13443 //int rnum;
13445 //rnum = rand32()&7;
13446 if(common_try_upper(NULL) || common_try_block(NULL) || common_try_runattack(NULL) ||
13447 //(rnum < 2 && common_try_freespecial(NULL)) ||
13448 common_try_normalattack(NULL) || common_try_jumpattack(NULL)) {
13449 self->running = 0;
13450 return 1;
13452 return 0; // nothing to do? so go to next think step
13455 // A.I. characters do a throw
13456 void common_throw() {
13457 if(self->animating)
13458 return; // just play the throw animation
13460 set_idle(self);
13462 // we have done the throw, return to A.I. root
13463 self->takeaction = NULL;
13466 // toss the grabbed one
13467 void dothrow() {
13468 entity *other;
13469 self->xdir = self->zdir = 0;
13470 other = self->link;
13471 if(other == NULL) //change back to idle, or we will get stuck here
13473 set_idle(self);
13474 self->takeaction = NULL; // A.I. root again
13475 return;
13478 if(other->modeldata.throwheight)
13479 toss(other, other->modeldata.throwheight);
13480 else
13481 toss(other, other->modeldata.jumpheight);
13483 other->direction = self->direction;
13484 other->projectile = 2;
13485 other->xdir = (other->direction) ? (-other->modeldata.throwdist) : (other->modeldata.throwdist);
13487 if(autoland == 1 && validanim(other, ANI_LAND))
13488 other->damage_on_landing = -1;
13489 else
13490 other->damage_on_landing = self->modeldata.throwdamage;
13492 set_fall(other, ATK_NORMAL, 0, self, 0, 0, 0, 0, 0, 0);
13493 ent_set_anim(self, ANI_THROW, 0);
13494 ent_unlink(other);
13496 other->takeaction = common_fall;
13497 self->takeaction = common_throw;
13501 // Waiting until throw frame reached
13502 void common_throw_wait() {
13503 if(!self->link) {
13504 set_idle(self);
13505 self->takeaction = NULL; // A.I. root again
13506 return;
13509 self->releasetime += THINK_SPEED; //extend release time
13511 if(self->animpos != self->modeldata.throwframewait)
13512 return;
13514 dothrow();
13518 void common_prethrow() {
13519 self->running = 0; // Quits running if grabbed by opponent
13521 // Just check if we're still grabbed...
13522 if(self->link)
13523 return;
13525 set_idle(self);
13527 self->takeaction = NULL; // A.I. root again
13530 // warp to its parent entity, just like skeletons in Diablo 2
13531 void npc_warp() {
13532 if(!self->parent)
13533 return;
13534 self->z = self->parent->z;
13535 self->x = self->parent->x;
13536 self->a = self->parent->a;
13537 self->xdir = self->zdir = 0;
13538 self->base = self->parent->base;
13539 self->tossv = 0;
13541 if(validanim(self, ANI_RESPAWN)) {
13542 ent_set_anim(self, ANI_RESPAWN, 0);
13543 self->takeaction = common_spawn;
13544 } else if(validanim(self, ANI_SPAWN)) {
13545 ent_set_anim(self, ANI_SPAWN, 0);
13546 self->takeaction = common_spawn;
13550 int adjust_grabposition(entity * ent, entity * other, float dist, int grabin) {
13551 float x1, z1, x2, z2, a, x;
13552 int wall1, wall2;
13553 a = ent->a;
13554 if(grabin == 1) {
13555 x1 = ent->x;
13556 z1 = z2 = ent->z;
13557 x2 = ent->x + ((other->x > ent->x) ? dist : -dist);
13558 } else {
13559 x = (ent->x + other->x) / 2;
13560 x1 = x + ((ent->x >= other->x) ? (dist / 2) : (-dist / 2));
13561 x2 = x + ((other->x > ent->x) ? (dist / 2) : (-dist / 2));
13562 z1 = z2 = (ent->z + other->z) / 2;
13565 if(ent->modeldata.subject_to_screen > 0 && (x1 < advancex || x1 > advancex + videomodes.hRes))
13566 return 0;
13567 else if(other->modeldata.subject_to_screen > 0 && (x2 < advancex || x2 > advancex + videomodes.hRes))
13568 return 0;
13570 wall1 = checkwall_below(x1, z1, 9999999);
13571 wall2 = checkwall_below(x2, z2, 9999999);
13572 if(wall1 < 0 && wall2 >= 0)
13573 return 0;
13574 else if(wall1 >= 0 && wall2 < 0)
13575 return 0;
13576 else if(wall1 >= 0 && level->walls[wall1].alt > a)
13577 return 0;
13578 else if(wall2 >= 0 && level->walls[wall2].alt > a)
13579 return 0;
13580 else if(wall1 >= 0 && wall2 >= 0 && level->walls[wall1].alt != level->walls[wall2].alt)
13581 return 0;
13583 if(wall1 < 0 && checkhole(x1, z1))
13584 return 0;
13585 if(wall2 < 0 && checkhole(x2, z2))
13586 return 0;
13588 ent->x = x1;
13589 ent->z = z1;
13590 other->x = x2;
13591 other->z = z2;
13592 //other->a = ent->a;
13593 //other->base = ent->base;
13594 return 1;
13598 int common_trymove(float xdir, float zdir) {
13599 entity *other = NULL;
13600 int wall, heightvar;
13601 float x, z, oxdir, ozdir;
13603 if(!xdir && !zdir)
13604 return 0;
13606 oxdir = xdir;
13607 ozdir = zdir;
13609 // entity is grabbed by other
13610 if(self->link && self->link->grabbing==self && self->link->grabwalking)
13612 return 1; // just return so we don't have to check twice
13613 } */
13615 x = self->x + xdir;
13616 z = self->z + zdir;
13617 // -----------bounds checking---------------
13618 // Subjec to Z and out of bounds? Return to level!
13619 if(self->modeldata.subject_to_minz > 0) {
13620 if(zdir && z < PLAYER_MIN_Z) {
13621 zdir = PLAYER_MIN_Z - self->z;
13622 execute_onblockz_script(self);
13626 if(self->modeldata.subject_to_maxz > 0) {
13627 if(zdir && z > PLAYER_MAX_Z) {
13628 zdir = PLAYER_MAX_Z - self->z;
13629 execute_onblockz_script(self);
13632 // End of level is blocked?
13633 if(level->exit_blocked) {
13634 if(x > level->width - 30 - (PLAYER_MAX_Z - z)) {
13635 xdir = level->width - 30 - (PLAYER_MAX_Z - z) - self->x;
13638 // screen checking
13639 if(self->modeldata.subject_to_screen > 0) {
13640 if(x < advancex + 10) {
13641 xdir = advancex + 10 - self->x;
13642 execute_onblocks_script(self); //Screen block event.
13643 } else if(x > advancex + (videomodes.hRes - 10)) {
13644 xdir = advancex + (videomodes.hRes - 10) - self->x;
13645 execute_onblocks_script(self); //Screen block event.
13649 if(!xdir && !zdir)
13650 return 0;
13651 x = self->x + xdir;
13652 z = self->z + zdir;
13654 //-----------end of bounds checking-----------
13656 //-------------hole checking ---------------------
13657 // Don't walk into a hole or walk off platforms into holes
13658 if(self->modeldata.type != TYPE_PLAYER && self->idling &&
13659 (!self->animation->seta || self->animation->seta[self->animpos] < 0) &&
13660 self->modeldata.subject_to_hole > 0 && !inair(self) && !(self->modeldata.aimove & AIMOVE2_IGNOREHOLES)) {
13661 if(xdir && checkhole(x, self->z) && checkwall(x, self->z) < 0
13663 (((other = check_platform(x, self->z))
13664 && self->base < other->a + other->animation->platform[other->animpos][7]) || other == NULL)) {
13665 xdir = 0;
13667 if(zdir && checkhole(self->x, z) && checkwall(self->x, z) < 0
13669 (((other = check_platform(self->x, z))
13670 && self->base < other->a + other->animation->platform[other->animpos][7]) || other == NULL)) {
13671 zdir = 0;
13675 if(!xdir && !zdir)
13676 return 0;
13677 x = self->x + xdir;
13678 z = self->z + zdir;
13679 //-----------end of hole checking---------------
13681 //--------------obstacle checking ------------------
13682 if(self->modeldata.subject_to_obstacle > 0 /*&& !inair(self) */ ) {
13683 if((other = find_ent_here(self, x, self->z, (TYPE_OBSTACLE | TYPE_TRAP))) &&
13684 (xdir > 0 ? other->x > self->x : other->x < self->x) &&
13685 (!other->animation->platform || !other->animation->platform[other->animpos][7])) {
13686 xdir = 0;
13687 execute_onblocko_script(self, other);
13689 if((other = find_ent_here(self, self->x, z, (TYPE_OBSTACLE | TYPE_TRAP))) &&
13690 (zdir > 0 ? other->z > self->z : other->z < self->z) &&
13691 (!other->animation->platform || !other->animation->platform[other->animpos][7])) {
13692 zdir = 0;
13693 execute_onblocko_script(self, other);
13697 if(!xdir && !zdir)
13698 return 0;
13699 x = self->x + xdir * 2;
13700 z = self->z + zdir * 2;
13702 //-----------end of obstacle checking--------------
13704 // ---------------- platform checking----------------
13706 if(self->animation->height)
13707 heightvar = self->animation->height;
13708 else
13709 heightvar = self->modeldata.height;
13711 // Check for obstacles with platform code and adjust base accordingly
13712 if(self->modeldata.subject_to_platform > 0
13713 && (other = check_platform_between(x, z, self->a, self->a + heightvar))) {
13714 if(xdir > 0 ? other->x > self->x : other->x < self->x) {
13715 xdir = 0;
13717 if(zdir > 0 ? other->z > self->z : other->z < self->z) {
13718 zdir = 0;
13722 if(!xdir && !zdir)
13723 return 0;
13724 x = self->x + xdir;
13725 z = self->z + zdir;
13727 //-----------end of platform checking------------------
13729 // ------------------ wall checking ---------------------
13730 if(self->modeldata.subject_to_wall) {
13731 if((wall = checkwall(x, self->z)) >= 0 && level->walls[wall].alt > self->a) {
13732 if(xdir > 0.5) {
13733 xdir = 0.5;
13734 } else if(xdir < -0.5) {
13735 xdir = -0.5;
13737 if((wall = checkwall(self->x + xdir, self->z)) >= 0 && level->walls[wall].alt > self->a) {
13738 xdir = 0;
13739 execute_onblockw_script(self, 1, (double) level->walls[wall].alt);
13742 if((wall = checkwall(self->x, z)) >= 0 && level->walls[wall].alt > self->a) {
13743 if(zdir > 0.5) {
13744 zdir = 0.5;
13745 } else if(zdir < -0.5) {
13746 zdir = -0.5;
13748 if((wall = checkwall(self->x, self->z + zdir)) >= 0 && level->walls[wall].alt > self->a) {
13749 zdir = 0;
13750 execute_onblockw_script(self, 2, (double) level->walls[wall].alt);
13756 if(!xdir && !zdir)
13757 return 0;
13758 x = self->x + xdir;
13759 z = self->z + zdir;
13760 //----------------end of wall checking--------------
13762 //------------------ grab/throw checking------------------
13763 if((validanim(self, ANI_THROW) ||
13764 validanim(self, ANI_GRAB)) && self->idling &&
13765 (other = find_ent_here(self, x, z, self->modeldata.hostile)) &&
13766 cangrab(self, other) && adjust_grabposition(self, other, self->modeldata.grabdistance, 0)) {
13767 self->direction = (self->x < other->x);
13769 set_opponent(other, self);
13770 ents_link(self, other);
13771 other->attacking = 0;
13772 self->idling = 0;
13773 self->running = 0;
13775 self->xdir = self->zdir = other->xdir = other->zdir = 0;
13776 if(validanim(self, ANI_GRAB)) {
13777 other->direction = !self->direction;
13778 ent_set_anim(self, ANI_GRAB, 0);
13779 set_pain(other, -1, 0); //set grabbed animation
13780 self->attacking = 0;
13781 memset(self->combostep, 0, 5 * sizeof(int));
13782 other->takeaction = common_grabbed;
13783 self->takeaction = common_grab;
13784 other->stalltime = borTime + GRAB_STALL;
13785 self->releasetime = borTime + (GAME_SPEED / 2);
13787 // use original throw code if throwframewait not present, kbandressen 10/20/06
13788 else if(self->modeldata.throwframewait == -1)
13789 dothrow();
13790 // otherwise enemy_throw_wait will be used, kbandressen 10/20/06
13791 else {
13792 other->direction = !self->direction;
13793 ent_set_anim(self, ANI_THROW, 0);
13794 set_pain(other, -1, 0); // set grabbed animation
13796 other->takeaction = common_prethrow;
13797 self->takeaction = common_throw_wait;
13799 return 0;
13801 // --------------- end of grab/throw checking ------------------------
13803 // do move and return
13804 self->x += xdir;
13805 self->z += zdir;
13807 if(xdir)
13808 execute_onmovex_script(self); //X move event.
13809 if(zdir)
13810 execute_onmovez_script(self); //Z move event.
13811 return 2 - (xdir == oxdir && zdir == ozdir); // return 2 for some checks
13814 // enemies run off after attack
13815 void common_runoff() {
13816 entity *target = normal_find_target(-1);
13818 if(target == NULL)
13819 return; // There are no players?
13820 if(!self->modeldata.noflip)
13821 self->direction = (self->x < target->x);
13822 if(self->direction)
13823 self->xdir = -self->modeldata.speed / 2;
13824 else
13825 self->xdir = self->modeldata.speed / 2;
13827 self->zdir = 0;
13829 adjust_walk_animation(target);
13831 if(borTime > self->stalltime)
13832 self->takeaction = NULL; // OK, back to A.I. root
13836 void common_stuck_underneath() {
13837 if(player[(int) self->playerindex].keys & FLAG_MOVELEFT) {
13838 player[(int) self->playerindex].playkeys -= FLAG_MOVELEFT;
13839 self->direction = 0;
13840 } else if(player[(int) self->playerindex].keys & FLAG_MOVERIGHT) {
13841 player[(int) self->playerindex].playkeys -= FLAG_MOVERIGHT;
13842 self->direction = 1;
13844 if(player[(int) self->playerindex].keys & FLAG_ATTACK && validanim(self, ANI_DUCKATTACK)) {
13845 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
13846 set_attacking(self);
13847 self->xdir = self->zdir = 0;
13848 self->combostep[0] = 0;
13849 self->running = 0;
13850 ent_set_anim(self, ANI_DUCKATTACK, 0);
13851 self->takeaction = common_attack_proc;
13852 return;
13854 if((player[(int) self->playerindex].keys & FLAG_MOVEDOWN) && (player[(int) self->playerindex].keys & FLAG_JUMP)
13855 && validanim(self, ANI_SLIDE)) {
13856 player[(int) self->playerindex].playkeys -= FLAG_MOVEDOWN;
13857 player[(int) self->playerindex].playkeys -= FLAG_JUMP;
13858 set_attacking(self);
13859 self->xdir = self->zdir = 0;
13860 self->combostep[0] = 0;
13861 self->running = 0;
13862 ent_set_anim(self, ANI_SLIDE, 0);
13863 self->takeaction = common_attack_proc;
13864 return;
13866 if(!check_platform_between(self->x, self->z, self->a, self->a + self->modeldata.height)) {
13867 set_idle(self);
13868 self->takeaction = NULL;
13869 return;
13874 // finish attacking, do something
13875 void common_attack_finish() {
13876 entity *target;
13878 self->xdir = self->zdir = 0;
13880 if(self->modeldata.type == TYPE_PLAYER) {
13881 set_idle(self);
13882 self->takeaction = NULL;
13883 return;
13886 target = normal_find_target(-1);
13888 if(target && !self->modeldata.nomove && diff(self->x, target->x) < 80 && (rand32() & 3)) {
13889 common_walk_anim(self);
13890 //ent_set_anim(self, ANI_WALK, 0);
13891 self->idling = 1;
13892 self->takeaction = common_runoff;
13893 } else {
13894 set_idle(self);
13895 self->takeaction = NULL;
13898 self->stalltime = borTime + GAME_SPEED - self->modeldata.aggression;
13902 //while playing attack animation
13903 void common_attack_proc() {
13905 if(self->animating || diff(self->a, self->base) >= 4)
13906 return;
13908 if(self->tocost) { // Enemy was hit with a special so go ahead and subtract life
13909 if(check_energy(1, self->animnum)) {
13910 self->mp -= self->animation->energycost[0];
13911 } else
13912 self->health -= self->animation->energycost[0];
13913 self->tocost = 0; // Life is subtracted, so go ahead and reset the flag
13916 if(self == smartbomber) { // Player is done with the special animation, so unfreeze and execute a smart bomb
13917 smart_bomb(self, self->modeldata.smartbomb);
13918 smartbomber = NULL;
13920 if(self->reactive == 1) {
13921 subtract_shot();
13922 self->reactive = 0;
13924 self->attacking = 0;
13925 // end of attack proc
13926 common_attack_finish();
13930 // dispatch A.I. attack
13931 int common_attack() {
13932 int aiattack;
13934 if(self->modeldata.aiattack == -1)
13935 return 0;
13937 aiattack = self->modeldata.aiattack & MASK_AIATTACK1;
13939 switch (aiattack) {
13940 case AIATTACK1_LONG:
13941 case AIATTACK1_MELEE:
13942 case AIATTACK1_NOATTACK:
13943 return 0;
13944 default: // this is the only available attack style by now
13945 return inair(self) ? 0 : normal_attack();
13949 //maybe used many times, so make a function
13950 // A.I. characters will check if there's a wall infront, and jump onto it if possible
13951 // return 1 if jump
13952 int common_try_jump() {
13953 float xdir, zdir;
13954 int wall, j = 0;
13955 float rmin, rmax;
13957 if(validanim(self, ANI_JUMP)) //Can jump?
13959 //Check to see if there is a wall within jumping distance and within a jumping height
13960 xdir = 0;
13961 wall = -1;
13962 rmin = (float) self->modeldata.animation[ANI_JUMP]->range[0];
13963 rmax = (float) self->modeldata.animation[ANI_JUMP]->range[1];
13964 if(self->direction)
13965 xdir = self->x + rmin;
13966 else
13967 xdir = self->x - rmin;
13968 //check z jump
13969 if(self->modeldata.jumpmovez)
13970 zdir = self->z + self->zdir;
13971 else
13972 zdir = self->z;
13974 if((wall = checkwall_below(xdir, zdir, 999999)) >= 0 &&
13975 level->walls[wall].alt <= self->a + rmax && !inair(self) && self->a < level->walls[wall].alt) {
13976 j = 1;
13977 } else if(checkhole(self->x + (self->direction ? 2 : -2), zdir) &&
13978 checkwall(self->x + (self->direction ? 2 : -2), zdir) < 0 &&
13979 !checkhole(self->x + (self->direction ? rmax : -rmax), zdir)) {
13980 j = 1;
13985 Damon V. Caskey
13986 03292010
13987 AI can will check its RUNJUMP range if JUMP can't reach. Code is pretty redundant,
13988 can probably be moved to a function later.
13990 if(!j && validanim(self, ANI_RUNJUMP)) //Jump check failed and can run jump?
13992 //Check for wall in range of RUNJUMP.
13993 xdir = 0;
13994 wall = -1;
13995 rmin = (float) self->modeldata.animation[ANI_RUNJUMP]->range[0];
13996 rmax = (float) self->modeldata.animation[ANI_RUNJUMP]->range[1];
13997 if(self->direction)
13998 xdir = self->x + rmin;
13999 else
14000 xdir = self->x - rmin;
14001 //check z jump
14002 if(self->modeldata.jumpmovez)
14003 zdir = self->z + self->zdir;
14004 else
14005 zdir = self->z;
14007 if((wall = checkwall_below(xdir, zdir, 999999)) >= 0 &&
14008 level->walls[wall].alt <= self->a + rmax && !inair(self) && self->a < level->walls[wall].alt) {
14009 j = 2; //Set to perform runjump.
14011 //Check for pit in range of RUNJUMP.
14012 else if(checkhole(self->x + (self->direction ? 2 : -2), zdir) &&
14013 checkwall(self->x + (self->direction ? 2 : -2), zdir) < 0 &&
14014 !checkhole(self->x + (self->direction ? rmax : -rmax), zdir)) {
14015 j = 2; //Set to perform runjump.
14019 if(j) {
14020 if(self->running || j == 2) {
14021 if(validanim(self, ANI_RUNJUMP)) //Running or only within range of RUNJUMP?
14022 tryjump(self->modeldata.runjumpheight,
14023 self->modeldata.jumpspeed * self->modeldata.runjumpdist,
14024 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_RUNJUMP);
14025 else if(validanim(self, ANI_FORWARDJUMP))
14026 tryjump(self->modeldata.runjumpheight,
14027 self->modeldata.jumpspeed * self->modeldata.runjumpdist,
14028 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_FORWARDJUMP);
14029 else
14030 tryjump(self->modeldata.runjumpheight,
14031 self->modeldata.jumpspeed * self->modeldata.runjumpdist,
14032 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_JUMP);
14033 } else {
14034 if(validanim(self, ANI_FORWARDJUMP))
14035 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed,
14036 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_FORWARDJUMP);
14037 else
14038 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed,
14039 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_JUMP);
14042 return 1;
14044 return 0;
14047 void adjust_walk_animation(entity * other) {
14048 if(self->running) {
14049 ent_set_anim(self, ANI_RUN, 0);
14050 return;
14052 //reset the walk animation
14053 if(((!other && self->zdir < 0) || (other && self->z > other->z)) && validanim(self, ANI_UP))
14054 common_up_anim(self); //ent_set_anim(self, ANI_UP, 0);
14055 else if(((!other && self->zdir > 0) || (other && other->z > self->z)) && validanim(self, ANI_DOWN))
14056 common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0);
14057 else if((self->direction ? self->xdir < 0 : self->xdir > 0) && validanim(self, ANI_BACKWALK))
14058 common_backwalk_anim(self); //ent_set_anim(self, ANI_BACKWALK, 0);
14059 else
14060 common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0);
14062 if((self->direction ? self->xdir < 0 : self->xdir > 0) && self->animnum != ANI_BACKWALK)
14063 self->animating = -1;
14064 else
14065 self->animating = 1;
14068 //may be used many times, so make a function
14069 // try to move towards the item
14070 int common_try_pick(entity * other) {
14071 // if there's an item to pick up, move towards it.
14072 float maxspeed = self->modeldata.speed * 1.5;
14073 float dx = diff(self->x, other->x);
14074 float dz = diff(self->z, other->z);
14076 if(other == NULL || self->modeldata.nomove)
14077 return 0;
14079 if(!dz && !dx)
14080 self->xdir = self->zdir = 0;
14081 else {
14082 self->xdir = maxspeed * dx / (dx + dz);
14083 self->zdir = maxspeed * dz / (dx + dz);
14085 if(self->x > other->x)
14086 self->xdir = -self->xdir;
14087 if(self->z > other->z)
14088 self->zdir = -self->zdir;
14090 self->running = 0;
14092 adjust_walk_animation(other);
14094 return 1;
14097 void checkpathblocked() {
14098 if(self->stalltime >= borTime) {
14099 if(self->pathblocked > 10) {
14100 self->xdir = self->zdir = 0;
14101 set_idle(self);
14102 self->pathblocked = 0;
14103 self->stalltime = borTime + GAME_SPEED / 4;
14108 //may be used many times, so make a function
14109 // try to move towards the target
14110 int common_try_chase(entity * target) {
14111 // start chasing the target
14112 float maxspeed;
14113 float dx, dz;
14114 int aitype;
14115 int rnum;
14117 if(target == NULL || self->modeldata.nomove)
14118 return 0;
14120 dx = diff(self->x, target->x);
14121 dz = diff(self->z, target->z);
14123 aitype = self->modeldata.aimove & rand32();
14124 if(!aitype)
14125 aitype = self->modeldata.aimove;
14126 if(self->modeldata.subtype == SUBTYPE_CHASE)
14127 aitype |= AIMOVE1_CHASE;
14129 if((aitype & AIMOVE1_CHASEX) && dx < 20)
14130 aitype -= AIMOVE1_CHASEX;
14131 if((aitype & AIMOVE1_CHASEZ) && dz < 10)
14132 aitype -= AIMOVE1_CHASEZ;
14133 if((aitype & AIMOVE1_CHASE) && dz + dx < 20)
14134 aitype -= AIMOVE1_CHASE;
14136 if(!(aitype & (AIMOVE1_CHASEX | AIMOVE1_CHASEZ | AIMOVE1_CHASE)))
14137 return 0; // none available, exit
14139 rnum = rand32();
14140 // if target is far away, run instead of walk
14141 if((self->direction ? self->x < target->x : self->x > target->x) && (((aitype & AIMOVE1_CHASEX)
14142 && (dx > 200 || (rnum & 15) < 3)
14143 && validanim(self, ANI_RUN))
14144 || ((dx + dz > 200 || (rnum & 15) < 3)
14145 && !(aitype & AIMOVE1_CHASEZ)
14146 && dx > 2 * dz
14147 && validanim(self, ANI_RUN)))) {
14148 maxspeed = self->modeldata.runspeed;
14149 self->running = 1;
14150 } else {
14151 maxspeed = self->modeldata.speed;
14152 self->running = 0;
14155 if(!dz && !dx)
14156 self->xdir = self->zdir = 0;
14157 else if(aitype & AIMOVE1_CHASEX) { // wander in z direction, chase in x direction
14158 self->xdir = maxspeed;
14159 if(self->modeldata.runupdown || !self->running) {
14160 if(diff(target->z, self->z) > videomodes.vRes / 3)
14161 self->zdir =
14162 (target->z > self->z) ? self->modeldata.speed / 2 : -self->modeldata.speed / 2;
14163 else
14164 self->zdir = randf(1) - randf(1);
14166 } else if(aitype & AIMOVE1_CHASEZ) { // wander in x direction, chase in z direction
14167 if(diff(target->x, self->x) > videomodes.hRes / 2.5)
14168 self->xdir = (target->x > self->x) ? self->modeldata.speed : -self->modeldata.speed;
14169 else
14170 self->xdir = randf(1) - randf(1);
14171 self->zdir = maxspeed / 2;
14172 } else { // chase in x and z direction
14173 if(self->modeldata.runupdown || !self->running) {
14174 self->xdir = maxspeed * dx / (dx + dz);
14175 self->zdir = maxspeed * dz / (dx + dz);
14176 } else {
14177 self->xdir = maxspeed;
14178 self->zdir = 0;
14181 if(self->x > target->x && !(aitype & AIMOVE1_CHASEZ))
14182 self->xdir = -self->xdir;
14183 if(self->z > target->z && !(aitype & AIMOVE1_CHASEX))
14184 self->zdir = -self->zdir;
14186 adjust_walk_animation(target);
14188 return 1;
14191 //may be used many times, so make a function
14192 // minion follow his owner
14193 int common_try_follow() {
14194 // start chasing the target
14195 entity *target = NULL;
14196 float distance = 0;
14197 float maxspeed, dx, dz;
14199 target = self->parent;
14200 if(target == NULL || self->modeldata.nomove)
14201 return 0;
14203 dx = diff(self->x, target->x);
14204 dz = diff(self->z, target->z);
14205 distance = (float) ((validanim(self, ANI_IDLE)) ? self->modeldata.animation[ANI_IDLE]->range[0] : 100);
14207 if(dz + dx < distance)
14208 return 0;
14210 // if target is far away, run instead of walk
14211 if(dx + dz > 200 && dx > 2 * dz && validanim(self, ANI_RUN)) {
14212 maxspeed = self->modeldata.runspeed;
14213 self->running = 1;
14214 } else {
14215 maxspeed = self->modeldata.speed;
14216 self->running = 0;
14219 if(!dz && !dx)
14220 self->xdir = self->zdir = 0;
14221 else {
14222 if(self->modeldata.runupdown || !self->running) {
14223 self->xdir = maxspeed * dx / (dx + dz);
14224 self->zdir = maxspeed * dz / (dx + dz);
14225 } else {
14226 self->xdir = maxspeed;
14227 self->zdir = 0;
14231 if(self->x > target->x)
14232 self->xdir = -self->xdir;
14233 if(self->z > target->z)
14234 self->zdir = -self->zdir;
14236 adjust_walk_animation(target);
14238 return 1;
14241 // try to avoid the target
14242 // used by 'avoid avoidz avoidx
14243 int common_try_avoid(entity * target) {
14244 float maxspeed = 0;
14245 float dx, dz;
14247 int aitype = 0;
14249 if(target == NULL || self->modeldata.nomove)
14250 return 0;
14252 dx = diff(self->x, target->x);
14253 dz = diff(self->z, target->z);
14255 aitype = self->modeldata.aimove & rand32();
14256 if(!aitype)
14257 aitype = self->modeldata.aimove;
14259 if((aitype & AIMOVE1_AVOIDX) && dx > 100)
14260 aitype -= AIMOVE1_AVOIDX;
14261 if((aitype & AIMOVE1_AVOIDZ) && dz > 50)
14262 aitype -= AIMOVE1_AVOIDZ;
14263 if((aitype & AIMOVE1_AVOID) && dz + dx > 150)
14264 aitype -= AIMOVE1_AVOID;
14266 if(!(aitype & (AIMOVE1_AVOIDX | AIMOVE1_AVOIDZ | AIMOVE1_AVOIDX)))
14267 return 0; // none available, exit
14269 maxspeed = self->modeldata.speed;
14271 if(self->x < advancex - 10)
14272 self->xdir = maxspeed;
14273 else if(self->x > advancex + videomodes.hRes + 10)
14274 self->xdir = -maxspeed;
14275 else
14276 self->xdir = (self->x < target->x) ? (-maxspeed) : maxspeed;
14278 if((level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN && self->z < advancey - 5) ||
14279 ((level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) && self->z < -5)) {
14280 self->zdir = maxspeed / 2;
14281 } else
14282 if((level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN
14283 && self->z > advancey + videomodes.vRes + 5) || ((level->scrolldir == SCROLL_UP
14284 || level->scrolldir == SCROLL_DOWN)
14285 && self->z > videomodes.vRes + 5)) {
14286 self->zdir = -maxspeed / 2;
14287 } else
14288 self->zdir = (self->z < target->z) ? (-maxspeed / 2) : (maxspeed / 2);
14290 adjust_walk_animation(target);
14292 return 1;
14295 // wander completely
14296 int common_try_wandercompletely() {
14297 int walk = 0;
14298 int rnum = 0;
14300 if(self->modeldata.nomove)
14301 return 0;
14303 walk = 0;
14304 rnum = rand32();
14305 self->xdir = self->zdir = 0;
14306 if((rnum & 15) < 4) {
14307 // Move up
14308 self->zdir = -self->modeldata.speed / 2;
14309 } else if((rnum & 15) > 11) {
14310 // Move down
14311 self->zdir = self->modeldata.speed / 2;
14314 rnum = rand32();
14315 if((rnum & 15) < 4) {
14316 // Walk forward
14317 if(self->direction == 1)
14318 self->xdir = self->modeldata.speed;
14319 else
14320 self->xdir = -self->modeldata.speed;
14321 } else if((rnum & 15) > 11) {
14322 if(self->modeldata.noflip) {
14323 // Walk backward
14324 if(self->direction == 1)
14325 self->xdir = -self->modeldata.speed;
14327 else
14328 self->xdir = self->modeldata.speed;
14329 } else if(!validanim(self, ANI_TURN)) {
14330 // flip and Walk forward
14331 self->direction = !self->direction;
14332 if(self->direction == 1)
14333 self->xdir = self->modeldata.speed;
14334 else
14335 self->xdir = -self->modeldata.speed;
14336 } else
14337 self->direction = !self->direction;
14340 if(self->x < advancex - 10) {
14341 self->xdir = self->modeldata.speed;
14342 } else if(self->x > advancex + videomodes.hRes + 10) {
14343 self->xdir = -self->modeldata.speed;
14346 if(((self->xdir > 0 && !self->direction) || (self->xdir < 0 && self->direction)) && !self->modeldata.noflip)
14347 self->direction = !self->direction;
14349 if((level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN && self->z < advancey - 5) ||
14350 ((level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) && self->z < -5)) {
14351 self->zdir = self->modeldata.speed / 2;
14352 } else
14353 if((level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN
14354 && self->z > advancey + videomodes.vRes + 5) || ((level->scrolldir == SCROLL_UP
14355 || level->scrolldir == SCROLL_DOWN)
14356 && self->z > videomodes.vRes + 5)) {
14357 self->zdir = -self->modeldata.speed / 2;
14360 if(self->xdir || self->zdir) {
14361 if((self->xdir > 0 && !self->direction) || (self->xdir < 0 && self->direction))
14362 walk = -1;
14363 else
14364 walk = 1;
14367 if(walk) {
14368 adjust_walk_animation(NULL);
14370 return 1;
14372 return 0;
14375 // just wander around, face to the target
14376 int common_try_wander(entity * target) {
14377 int walk = 0;
14378 int rnum = rand32() & 7;
14380 if(target == NULL || self->modeldata.nomove)
14381 return 0;
14382 // Decide next direction... randomly.
14384 self->xdir = self->zdir = 0;
14386 if(diff(self->x, target->x) > videomodes.hRes / 2) {
14387 self->xdir = (self->x > target->x) ? -self->modeldata.speed : self->modeldata.speed;;
14388 walk = 1;
14389 } else if(rnum < 3) {
14390 self->xdir = self->modeldata.speed;
14391 walk = 1;
14392 } else if(rnum > 4) {
14393 self->xdir = -self->modeldata.speed;
14394 walk = 1;
14397 rnum = rand32() & 7;
14398 if(rnum < 2) {
14399 // Move up
14400 self->zdir = -self->modeldata.speed / 2;
14401 walk |= 1;
14402 } else if(rnum > 5) {
14403 // Move down
14404 self->zdir = self->modeldata.speed / 2;
14405 walk |= 1;
14408 if(walk) {
14409 adjust_walk_animation(target);
14410 } else {
14411 self->xdir = self->zdir = 0;
14412 set_idle(self);
14415 return 1;
14418 //A.I chracter pickup an item
14419 void common_pickupitem(entity * other) {
14420 int pickup = 0;
14421 //weapons
14422 if(self->weapent == NULL && isSubtypeWeapon(other) && validanim(self, ANI_GET)) {
14423 dropweapon(0); //don't bother dropping the previous one though, scine it won't pickup another
14424 self->weapent = other;
14425 set_weapon(self, other->modeldata.weapnum, 0);
14426 ent_set_anim(self, ANI_GET, 0);
14427 if(self->modeldata.animal) // UTunnels: well, ride, not get. :)
14429 self->direction = other->direction;
14430 self->x = other->x;
14431 self->z = other->z;
14433 set_getting(self);
14434 self->takeaction = common_get;
14435 self->xdir = self->zdir = 0; //stop moving
14436 pickup = 1;
14438 // projectiles
14439 else if(self->weapent == NULL && isSubtypeProjectile(other) && validanim(self, ANI_GET)) {
14440 dropweapon(0);
14441 self->weapent = other;
14442 ent_set_anim(self, ANI_GET, 0);
14443 set_getting(self);
14444 self->takeaction = common_get;
14445 self->xdir = self->zdir = 0; //stop moving
14446 pickup = 1;
14448 // other items
14449 else if(!isSubtypeWeapon(other) && !isSubtypeProjectile(other)) {
14450 if(validanim(self, ANI_GET) && isSubtypeTouch(other) && canBeDamaged(other, self)) {
14451 ent_set_anim(self, ANI_GET, 0);
14452 set_getting(self);
14453 self->takeaction = common_get;
14454 self->xdir = self->zdir = 0; //stop moving
14456 if(other->health) {
14457 self->health += other->health;
14458 if(self->health > self->modeldata.health)
14459 self->health = self->modeldata.health;
14460 other->health = 0;
14461 //sound_play_sample(SAMPLE_GET, 0, savedata.effectvol,savedata.effectvol, 100);
14463 // else if, TODO: other effects
14464 // kill that item
14465 other->takeaction = suicide;
14466 other->nextthink = borTime + GAME_SPEED * 3;
14467 pickup = 1;
14469 // hide it
14470 if(pickup) {
14471 other->z = 100000;
14472 execute_didhit_script(other, self, 0, 0, other->modeldata.subtype, 0, 0, 0, 0, 0); //Execute didhit script as if item "hit" collecter to allow easy item scripting.
14476 //walk/run/pickup/jump etc
14477 //Used by normal A.I. pattern
14478 int normal_move() {
14479 entity *other = NULL; //item
14480 entity *target = NULL; //hostile target
14481 entity *owner = NULL;
14482 float seta;
14483 int predir;
14485 predir = self->direction;
14487 target = normal_find_target(-1); // confirm the target again
14488 other = normal_find_item(); // find an item
14489 owner = self->parent;
14491 if(!self->modeldata.noflip && !self->running) {
14492 if(other) //try to pick up an item, if possible
14493 self->direction = (self->x < other->x);
14494 else if(target)
14495 self->direction = (self->x < target->x);
14496 else if(owner)
14497 self->direction = (self->x < owner->x);
14499 //turn back if we have a turn animation
14500 if(self->direction != predir && validanim(self, ANI_TURN)) {
14501 self->direction = !self->direction;
14502 ent_set_anim(self, ANI_TURN, 0);
14503 self->xdir = self->zdir = 0;
14504 self->takeaction = common_turn;
14505 return 1;
14508 if(common_try_jump())
14509 return 1; //need to jump? so quit
14511 checkpathblocked();
14513 // judge next move if stalltime is expired
14514 if(self->stalltime < borTime) {
14515 if(other) {
14516 // try walking to the item
14517 common_try_pick(other);
14518 } else if(target && (self->modeldata.subtype == SUBTYPE_CHASE ||
14519 (self->modeldata.type == TYPE_NPC && self->parent))) {
14520 // try chase a target
14521 common_try_chase(target);
14522 } else if(target) {
14523 // just wander around
14524 common_try_wander(target);
14525 } else if(!common_try_follow()) {
14526 // no target or item, just relex and idle
14527 self->xdir = self->zdir = 0;
14528 set_idle(self);
14530 //end of if
14532 self->stalltime = borTime + GAME_SPEED - self->modeldata.aggression;
14534 //pick up the item if possible
14535 if((other && other == find_ent_here(self, self->x, self->z, TYPE_ITEM)) && other->animation->vulnerable[other->animpos]) //won't pickup an item that is not previous one
14537 seta = (float) (self->animation->seta ? self->animation->seta[self->animpos] : -1);
14538 if(diff(self->a - (seta >= 0) * seta, other->a) < 0.1)
14539 common_pickupitem(other);
14542 return 1;
14545 //walk/run/pickup/jump etc
14546 //Used by avoid A.I. pattern
14547 int avoid_move() {
14549 entity *other = NULL; //item
14550 entity *target = NULL; //hostile target
14551 entity *owner = NULL;
14552 float seta;
14553 int predir;
14555 predir = self->direction;
14556 target = normal_find_target(-1); // confirm the target again
14557 other = normal_find_item(); // find an item
14558 owner = self->parent;
14560 if(!self->modeldata.noflip && !self->running) {
14561 if(target)
14562 self->direction = (self->x < target->x);
14563 else if(other) //try to pick up an item, if possible
14564 self->direction = (self->x < other->x);
14565 else if(owner)
14566 self->direction = (self->x < owner->x);
14568 //turn back if we have a turn animation
14569 if(self->direction != predir && validanim(self, ANI_TURN)) {
14570 self->direction = !self->direction;
14571 ent_set_anim(self, ANI_TURN, 0);
14572 self->xdir = self->zdir = 0;
14573 self->takeaction = common_turn;
14574 return 1;
14577 if(common_try_jump())
14578 return 1; //need to jump? so quit
14580 checkpathblocked();
14582 // judge next move if stalltime is expired
14583 if(self->stalltime < borTime) {
14584 if(target != NULL && (other == NULL ||
14585 (other && diff(self->x, other->x) + diff(self->z, other->z) >
14586 diff(self->x, target->x) + diff(self->z, target->z)))) {
14587 // avoid the target
14588 if(!common_try_avoid(target))
14589 common_try_wander(target);
14591 } else if(other) {
14592 // try walking to the item
14593 common_try_pick(other);
14594 } else if(!common_try_follow()) {
14595 // no target or item, just relex and idle
14596 self->xdir = self->zdir = 0;
14597 set_idle(self);
14599 //end of if
14601 self->stalltime = borTime + GAME_SPEED - self->modeldata.aggression;
14603 //pick up the item if possible
14604 if((other && other == find_ent_here(self, self->x, self->z, TYPE_ITEM)) && other->animation->vulnerable[other->animpos]) //won't pickup an item that is not previous one
14606 seta = (float) (self->animation->seta ? self->animation->seta[self->animpos] : -1);
14607 if(diff(self->a - (seta >= 0) * seta, other->a) < 0.1)
14608 common_pickupitem(other);
14611 return 1;
14614 //walk/run/pickup/jump etc
14615 //Used by chase A.I. pattern
14616 int chase_move() {
14618 entity *other = NULL; //item
14619 entity *target = NULL; //hostile target
14620 entity *owner = NULL;
14621 float seta;
14622 int predir;
14624 predir = self->direction;
14625 target = normal_find_target(-1); // confirm the target again
14626 other = normal_find_item(); // find an item
14627 owner = self->parent;
14629 if(!self->modeldata.noflip && !self->running) {
14630 if(target)
14631 self->direction = (self->x < target->x);
14632 else if(other) //try to pick up an item, if possible
14633 self->direction = (self->x < other->x);
14634 else if(owner)
14635 self->direction = (self->x < owner->x);
14637 //turn back if we have a turn animation
14638 if(self->direction != predir && validanim(self, ANI_TURN)) {
14639 self->direction = !self->direction;
14640 ent_set_anim(self, ANI_TURN, 0);
14641 self->xdir = self->zdir = 0;
14642 self->takeaction = common_turn;
14643 return 1;
14646 if(common_try_jump())
14647 return 1; //need to jump? so quit
14649 checkpathblocked();
14651 // judge next move if stalltime is expired
14652 if(self->stalltime < borTime) {
14653 if(target != NULL && (other == NULL ||
14654 (other && diff(self->x, other->x) + diff(self->z, other->z) >
14655 diff(self->x, target->x) + diff(self->z, target->z)))) {
14656 // chase the target
14657 if(!common_try_chase(target))
14658 common_try_wander(target);
14660 } else if(other) {
14661 // try walking to the item
14662 common_try_pick(other);
14663 } else if(!common_try_follow()) {
14664 // no target or item, just relex and idle
14665 self->xdir = self->zdir = 0;
14666 set_idle(self);
14668 //end of if
14670 self->stalltime = borTime + GAME_SPEED - self->modeldata.aggression;
14673 //pick up the item if possible
14674 if((other && other == find_ent_here(self, self->x, self->z, TYPE_ITEM)) && other->animation->vulnerable[other->animpos]) //won't pickup an item that is not previous one
14676 seta = (float) (self->animation->seta ? self->animation->seta[self->animpos] : -1);
14677 if(diff(self->a - (seta >= 0) * seta, other->a) < 0.1)
14678 common_pickupitem(other);
14681 return 1;
14684 //Used by wander A.I. pattern
14685 // these guys ignore target's position, wandering around
14686 int wander_move() {
14688 entity *other = NULL; //item
14689 //entity* target = NULL;//hostile target
14690 float seta;
14691 int predir;
14693 predir = self->direction;
14694 //target = normal_find_target(); // confirm the target again
14695 other = normal_find_item(); // find an item
14697 if(!self->modeldata.noflip && other && !self->running) {
14698 //try to pick up an item, if possible
14699 // just ignore target, but will still chase item
14700 self->direction = (self->x < other->x);
14702 //turn back if we have a turn animation
14703 if(self->direction != predir && validanim(self, ANI_TURN)) {
14704 self->direction = !self->direction;
14705 ent_set_anim(self, ANI_TURN, 0);
14706 self->xdir = self->zdir = 0;
14707 self->takeaction = common_turn;
14708 return 1;
14711 if(common_try_jump())
14712 return 1; //need to jump? so quit
14714 checkpathblocked();
14716 // judge next move if stalltime is expired
14717 if(self->stalltime < borTime) {
14718 if(other) {
14719 // try walking to the item
14720 common_try_pick(other);
14722 // let's wander around
14723 else if(!common_try_wandercompletely()) {
14724 // no target or item, just relex and idle
14725 self->xdir = self->zdir = 0;
14726 set_idle(self);
14728 //turn back if we have a turn animation
14729 if(self->direction != predir && validanim(self, ANI_TURN)) {
14730 self->direction = !self->direction;
14731 ent_set_anim(self, ANI_TURN, 0);
14732 self->xdir = self->zdir = 0;
14733 self->takeaction = common_turn;
14734 return 1;
14736 self->stalltime = borTime + GAME_SPEED - self->modeldata.aggression;
14738 //pick up the item if possible
14739 if((other && other == find_ent_here(self, self->x, self->z, TYPE_ITEM)) && other->animation->vulnerable[other->animpos]) //won't pickup an item that is not previous one
14741 seta = (float) (self->animation->seta ? self->animation->seta[self->animpos] : -1);
14742 if(diff(self->a - (seta >= 0) * seta, other->a) < 0.1)
14743 common_pickupitem(other);
14746 return 1;
14749 // for old bikers
14750 int biker_move() {
14752 if((self->direction) ? (self->x > advancex + (videomodes.hRes + 200)) : (self->x < advancex - 200)) {
14753 self->direction = !self->direction;
14754 self->attack_id = 0;
14755 self->z = (float) (PLAYER_MIN_Z + randf((float) (PLAYER_MAX_Z - PLAYER_MIN_Z)));
14756 sound_play_sample(samples.bike, 0, savedata.effectvol, savedata.effectvol, 100);
14757 if(self->modeldata.speed)
14758 self->xdir = (self->direction) ? (self->modeldata.speed) : (-self->modeldata.speed);
14759 else
14760 self->xdir =
14761 (self->direction) ? ((float) 1.7 +
14762 randf((float) 0.6)) : (-((float) 1.7 + randf((float) 0.6)));
14765 self->nextthink = borTime + THINK_SPEED / 2;
14766 return 1;
14769 // for common arrow types
14770 int arrow_move() {
14771 int wall;
14772 float dx;
14773 float dz;
14774 float maxspeed;
14775 entity *target = NULL;
14778 int osk = self->modeldata.offscreenkill?self->modeldata.offscreenkill:200; //TODO: temporary code here, needs refine
14780 if( (!self->direction && self->x < advancex - osk) || (self->direction && self->x > advancex + (videomodes.hRes+osk)) ||
14781 (level && level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN &&
14782 (self->z < advancey - 200 || self->z > advancey + (videomodes.vRes+200))) ||
14783 (level && (level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) &&
14784 (self->z < -osk || self->z > videomodes.vRes + osk)) )
14786 kill(self);
14787 return 0;
14788 } */
14790 // new subtype chase
14791 if(self->modeldata.subtype == SUBTYPE_CHASE) {
14792 target = homing_find_target(self->modeldata.hostile);
14794 if(target) {
14795 if(!self->modeldata.noflip)
14796 self->direction = (target->x > self->x);
14797 // start chasing the target
14798 dx = diff(self->x, target->x);
14799 dz = diff(self->z, target->z);
14800 maxspeed = self->modeldata.speed * 1.5;
14802 if(!dz && !dx)
14803 self->xdir = self->zdir = 0;
14804 else {
14805 self->xdir = maxspeed * dx / (dx + dz);
14806 self->zdir = maxspeed * dz / (dx + dz);
14808 if(self->direction != 1)
14809 self->xdir = -self->xdir;
14810 if(self->z > target->z)
14811 self->zdir = -self->zdir;
14812 } else {
14813 if(!self->xdir && !self->zdir) {
14814 if(self->direction == 0)
14815 self->xdir = -self->modeldata.speed;
14816 else if(self->direction == 1)
14817 self->xdir = self->modeldata.speed;
14820 if(!self->modeldata.nomove) {
14821 if(target && self->z > target->z && validanim(self, ANI_UP))
14822 common_up_anim(self); //ent_set_anim(self, ANI_UP, 0);
14823 else if(target && target->z > self->z && validanim(self, ANI_DOWN))
14824 common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0);
14825 else if(validanim(self, ANI_WALK))
14826 common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0);
14827 else {
14828 ent_set_anim(self, ANI_IDLE, 0);
14831 } else {
14832 // Now projectiles can have custom speeds
14833 if(self->direction == 0)
14834 self->xdir = -self->modeldata.speed;
14835 else if(self->direction == 1)
14836 self->xdir = self->modeldata.speed;
14839 if(level) {
14840 if((level->exit_blocked && self->x > level->width - 30 - (PLAYER_MAX_Z - self->z)) ||
14841 (self->modeldata.subject_to_wall && (wall = checkwall(self->x, self->z)) >= 0
14842 && self->a < level->walls[wall].alt)) {
14843 // Added so projectiles bounce off blocked exits
14844 if(validanim(self, ANI_FALL)) {
14845 self->attacking = 0;
14846 self->health -= 100000;
14847 self->projectile = 0;
14848 if(self->direction == 0)
14849 self->xdir = (float) -1.2;
14850 else if(self->direction == 1)
14851 self->xdir = (float) 1.2;
14852 self->takeaction = common_fall;
14853 self->damage_on_landing = 0;
14854 toss(self, 2.5 + randf(1));
14855 self->modeldata.no_adjust_base = 0;
14856 self->modeldata.subject_to_wall = self->modeldata.subject_to_platform =
14857 self->modeldata.subject_to_hole = self->modeldata.subject_to_gravity = 1;
14858 set_fall(self, ATK_NORMAL, 0, self, 100000, 0, 0, 0, 0, 0);
14862 if(self->ptype) {
14863 self->autokill = 1;
14864 self->nextthink = borTime + 1;
14865 } else
14866 self->nextthink = borTime + THINK_SPEED / 2;
14867 return 1;
14870 // for common bomb types
14871 int bomb_move() {
14872 /*if(!(self->x > level->width - 30 - (PLAYER_MAX_Z-self->z)))
14873 { */
14874 if(self->direction == 0)
14875 self->xdir = -self->modeldata.speed;
14876 else if(self->direction == 1)
14877 self->xdir = self->modeldata.speed;
14878 /*} */
14880 self->nextthink = borTime + THINK_SPEED / 2;
14882 if(inair(self) && self->toexplode == 1)
14883 return 1;
14885 self->tossv = 0; // Stop moving up/down
14886 self->modeldata.no_adjust_base = 1; // Stop moving up/down
14887 self->base = self->a;
14888 self->xdir = self->zdir = 0;
14890 sound_play_sample(self->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100);
14892 if(self->toexplode == 2 && validanim(self, ANI_ATTACK2)) {
14893 ent_set_anim(self, ANI_ATTACK2, 0); // If bomb never reaces the ground, play this
14894 } else {
14895 if(validanim(self, ANI_ATTACK1))
14896 ent_set_anim(self, ANI_ATTACK1, 0);
14898 // hit something, just make it an explosion animation.
14899 self->modeldata.subject_to_wall = 0;
14900 self->modeldata.subject_to_platform = 1;
14901 self->modeldata.subject_to_hole = 0;
14902 self->takeaction = bomb_explode;
14903 return 1;
14906 int star_move() {
14907 int wall;
14909 if(self->x < advancex - 80 || self->x > advancex + (videomodes.hRes + 80)
14910 || (self->base <= 0 && !self->modeldata.falldie)) {
14911 kill(self);
14912 return 0;
14915 self->base -= 2;
14916 self->a = self->base;
14918 if(validanim(self, ANI_FALL)) // Added so projectiles bounce off blocked exits
14920 if((level->exit_blocked && self->x > level->width - 30 - (PLAYER_MAX_Z - self->z)) ||
14921 ((wall = checkwall(self->x, self->z)) >= 0 && self->a < level->walls[wall].alt)) {
14922 self->attacking = 0;
14923 self->health -= 100000;
14924 self->projectile = 0;
14925 self->xdir = (self->direction) ? (-1.2) : 1.2;
14926 self->takeaction = common_fall;
14927 self->damage_on_landing = 0;
14928 toss(self, 2.5 + randf(1));
14929 set_fall(self, ATK_NORMAL, 0, self, 100000, 0, 0, 0, 0, 0);
14933 if(self->landed_on_platform || self->base <= 0) {
14934 self->health -= 100000;
14935 if(self->modeldata.nodieblink == 2)
14936 self->animating = 0;
14937 self->takeaction = common_lie;
14940 self->nextthink = borTime + THINK_SPEED / 2;
14941 return 1;
14945 //dispatch move patterns
14946 int common_move() {
14947 int aimove;
14948 int air = inair(self);
14949 if(self->modeldata.aimove == -1)
14950 return 0; // invalid value
14952 // get move pattern
14953 aimove = self->modeldata.aimove & MASK_AIMOVE1 & rand32();
14954 if(!aimove)
14955 aimove = self->modeldata.aimove & MASK_AIMOVE1;
14956 //if(stricmp(self->name, "os")==0) printf("%d\n", aimove);
14957 if(!aimove) { // normal move style, for common enemy/npc
14958 return air ? 0 : normal_move();
14959 } else if(aimove & (AIMOVE1_AVOID | AIMOVE1_AVOIDX | AIMOVE1_AVOIDZ)) { // try to avoid target
14960 return air ? 0 : avoid_move();
14961 } else if(aimove & (AIMOVE1_CHASE | AIMOVE1_CHASEX | AIMOVE1_CHASEZ)) { // try to chase target
14962 return air ? 0 : chase_move();
14963 } else if(aimove & AIMOVE1_WANDER) { // ignore target
14964 return air ? 0 : wander_move();
14965 } else if(aimove & AIMOVE1_BIKER) { // for biker subtype
14966 return biker_move();
14967 } else if(aimove & AIMOVE1_ARROW) { // for common straight-flying arrow
14968 return arrow_move();
14969 } else if(aimove & AIMOVE1_STAR) { // for a star, disappear when hit ground
14970 return star_move();
14971 } else if(aimove & AIMOVE1_BOMB) { // for a bomb, travel in a arc
14972 return bomb_move();
14973 } else if(aimove & AIMOVE1_NOMOVE) { // no move, just return
14974 return 0;
14977 return 0;
14980 // A.I root
14981 void common_think() {
14982 if(self->dead)
14983 return;
14985 // too far away , do a warp
14986 if(self->modeldata.subtype == SUBTYPE_FOLLOW && self->parent &&
14987 (diff(self->z, self->parent->z) > self->modeldata.animation[ANI_IDLE]->range[1] ||
14988 diff(self->x, self->parent->x) > self->modeldata.animation[ANI_IDLE]->range[1])) {
14989 self->takeaction = npc_warp;
14990 return;
14992 // rise? try rise attack
14993 if(self->drop && self->a == self->base && !self->tossv && validanim(self, ANI_RISEATTACK)
14994 && ((rand32() % (self->stalltime - borTime + 1)) < 3) && (self->health > 0 && borTime > self->staydown[2])) {
14995 common_try_riseattack();
14996 return;
14998 // Escape?
14999 if(self->link && !self->grabbing && !self->inpain && self->takeaction != common_prethrow &&
15000 borTime >= self->stalltime && validanim(self, ANI_SPECIAL)) {
15001 check_special();
15002 return;
15005 if(self->grabbing && !self->attacking) {
15006 common_grab_check();
15007 return;
15009 // Reset their escapecount if they aren't being spammed anymore.
15010 if(self->modeldata.escapehits && !self->inpain)
15011 self->escapecount = 0;
15013 // Enemies can now escape non-knockdown spammage (What a weird phrase)!
15014 if((self->escapecount > self->modeldata.escapehits) && !inair(self) && validanim(self, ANI_SPECIAL2)) {
15015 // Counter the player!
15016 check_costmove(ANI_SPECIAL2, 0);
15017 return;
15020 if(self->link)
15021 return;
15023 // idle, so try to attack or judge next move
15024 // dont move if fall into a hole or off a wall
15025 if(self->idling /*|| (self->animation->idle && self->animation->idle[self->animpos]) */ ) {
15026 if(common_attack())
15027 return;
15028 common_move();
15032 //////////////////////////////////////////////////////////////////////////
15034 void suicide() {
15035 if(borTime < self->stalltime)
15036 return;
15037 level_completed |= self->boss;
15038 kill(self);
15043 // Re-enter playfield
15044 // Used by player_fall and player_takedamage
15045 void player_die() {
15046 int playerindex = self->playerindex;
15047 if(!livescheat)
15048 --player[playerindex].lives;
15050 execute_pdie_script(playerindex);
15052 if(nomaxrushreset[4] >= 1)
15053 nomaxrushreset[playerindex] = player[playerindex].ent->rush[1];
15054 player[playerindex].ent = NULL;
15055 player[playerindex].spawnhealth = self->modeldata.health;
15056 player[playerindex].spawnmp = self->modeldata.mp;
15060 if(self->modeldata.nodieblink != 3)
15061 kill(self);
15062 else {
15063 self->think = NULL;
15064 self->takeaction = NULL;
15065 self->modeldata.type = TYPE_NONE;
15068 if(player[playerindex].lives <= 0) {
15069 if(!player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent) {
15070 timeleft = 10 * COUNTER_SPEED;
15071 if((!noshare && credits < 1)
15072 || (noshare && player[0].credits < 1 && player[1].credits < 1 && player[2].credits < 1
15073 && player[3].credits < 1))
15074 timeleft = COUNTER_SPEED / 2;
15076 if(self->modeldata.weaploss[0] <= 3)
15077 player[playerindex].weapnum = level->setweap;
15078 if(nomaxrushreset[4] != 2)
15079 nomaxrushreset[playerindex] = 0;
15080 return;
15081 } else {
15082 spawnplayer(playerindex);
15083 execute_respawn_script(playerindex);
15084 if(!nodropen) {
15085 control_rumble(playerindex, 125);
15086 drop_all_enemies();
15090 if(!level->noreset)
15091 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
15097 int player_trymove(float xdir, float zdir) {
15098 return common_trymove(xdir, zdir);
15101 int check_energy(int which, int ani) {
15102 int iCost[3]; //0 = Energycost[0] (amount of HP or MP needed), 1 = Cost type (MP, HP, both), 2 = Disable flag.
15103 int iType; //Entity type.
15105 if(self->modeldata.animation[ani]) //Does animation exist?
15107 iCost[2] = self->modeldata.animation[ani]->energycost[2]; //Get disable flag.
15108 iType = self->modeldata.type; //Get entity type.
15110 /* DC 05082010: It is now possible to individualy disable specials. In
15111 many cases (weapons in particular) this can help cut down the need for
15112 superflous models when differing abilities are desired for players,
15114 enemies, or npcs. */
15115 if(!(iCost[2] == iType //Disabled by type?
15116 || (iCost[2] == -1) //Disabled for all?
15117 || (iCost[2] == -2 && (iType == TYPE_ENEMY || iType == TYPE_NPC)) //Disabled for all AI?
15118 || (iCost[2] == -3 && (iType == TYPE_PLAYER || iType == TYPE_NPC)) //Disabled for players & NPCs?
15119 || (iCost[2] == -4 && (iType == TYPE_PLAYER || iType == TYPE_ENEMY)))) //Disabled for all AI?
15121 iCost[0] = self->modeldata.animation[ani]->energycost[0]; //Get energy cost amount
15122 iCost[1] = self->modeldata.animation[ani]->energycost[1]; //Get energy cost type.
15124 if(!self->seal || self->seal >= iCost[0]) //No seal or seal is less/same as energy cost?
15126 if(validanim(self, ani) && //Validate the animation one more time.
15127 ((which && //Check magic validity
15128 (iCost[1] != 2) && //2 = From health bar only, 0 from both
15129 (self->mp >= iCost[0])) || (!which && //Checks health validity
15130 (iCost[1] != 1) && //1 = From magic bar only, 0 from both
15131 (self->health > iCost[0])))) {
15132 return 1;
15133 } else {
15134 //DC 01232009
15135 //Tried putting the CANT animation here to keep code compacted, but won't work. I'll come back to this.
15136 //if (validanim(self,ani)){
15137 // ent_set_anim(self, ANI_CANT, 0);
15138 // self->takeaction = common_attack_proc;
15139 // player[(int)self->playerindex].playkeys = 0;
15141 return 0;
15146 return 0;
15150 int check_special() {
15151 if((!level->nospecial || level->nospecial == 3) &&
15152 !self->cantfire && (check_energy(0, ANI_SPECIAL) || check_energy(1, ANI_SPECIAL))) {
15153 set_attacking(self);
15154 memset(self->combostep, 0, sizeof(int) * 5);
15155 ent_unlink(self);
15157 if(self->modeldata.smartbomb && !self->modeldata.dofreeze) {
15158 smart_bomb(self, self->modeldata.smartbomb); // do smartbomb immediately if it doesn't freeze screen
15161 self->running = 0; // If special is executed while running, ceases to run
15162 self->xdir = self->zdir = 0;
15163 ent_set_anim(self, ANI_SPECIAL, 0);
15164 self->takeaction = common_attack_proc;
15166 if(self->modeldata.dofreeze)
15167 smartbomber = self; // Freezes the animations of all enemies/players while special is executed
15169 if(!nocost && !healthcheat) {
15170 if(check_energy(1, ANI_SPECIAL))
15171 self->mp -= self->modeldata.animation[ANI_SPECIAL]->energycost[0];
15172 else
15173 self->health -= self->modeldata.animation[ANI_SPECIAL]->energycost[0];
15176 return 1;
15178 return 0;
15182 // Check keys for special move. Used several times, so I func'd it.
15183 // 1-10-05 changed self->health>6 to self->health > self->modeldata.animation[ANI_SPECIAL]->energycost[0]
15184 int player_check_special() {
15185 u32 thekey = 0;
15187 if((!ajspecial || (ajspecial && !validanim(self, ANI_BLOCK))) &&
15188 (player[(int) self->playerindex].playkeys & FLAG_SPECIAL)) {
15189 thekey = FLAG_SPECIAL;
15190 } else if(ajspecial && ((player[(int) self->playerindex].playkeys & FLAG_JUMP) &&
15191 (player[(int) self->playerindex].keys & FLAG_ATTACK))) {
15192 thekey = FLAG_JUMP;
15193 } else
15194 return 0;
15196 if(check_special()) {
15197 self->stalltime = 0;
15198 player[(int) self->playerindex].playkeys -= thekey;
15199 return 1;
15200 } else {
15201 return 0;
15206 void common_land() {
15207 self->xdir = self->zdir = 0;
15208 if(self->animating)
15209 return;
15211 set_idle(self);
15212 self->takeaction = NULL;
15216 //animal run when you lost it 3 times by tails
15217 void runanimal() {
15218 common_walk_anim(self);
15219 //ent_set_anim(self, ANI_WALK, 0);
15221 if(self->x < advancex - 80 || self->x > advancex + (videomodes.hRes + 80)) {
15222 kill(self);
15223 return;
15226 if(self->direction)
15227 self->x += self->modeldata.speed;
15228 else
15229 self->x -= self->modeldata.speed;
15233 void player_blink() {
15234 self->blink = 1;
15235 if(borTime >= self->stalltime)
15236 player_die();
15240 void common_grabattack() {
15241 if(self->animating)
15242 return;
15244 self->attacking = 0;
15246 if(!(self->combostep[0] || self->combostep[1] ||
15247 self->combostep[2] || self->combostep[3] || self->combostep[4])) {
15248 ent_unlink(self);
15251 if(self->link) {
15252 self->attacking = 0;
15253 ent_set_anim(self, ANI_GRAB, 0);
15254 set_pain(self->link, -1, 0);
15255 update_frame(self, self->animation->numframes - 1);
15256 update_frame(self->link, self->link->animation->numframes - 1);
15257 self->takeaction = common_grab;
15258 self->link->takeaction = common_grabbed;
15259 } else {
15260 memset(self->combostep, 0, sizeof(int) * 5);
15261 set_idle(self);
15262 self->takeaction = NULL;
15266 // The vault.
15267 void common_vault() {
15268 if(!self->link) {
15269 set_idle(self);
15270 self->takeaction = NULL;
15271 return;
15273 if(!self->animating) {
15274 self->attacking = 0;
15275 self->direction = !self->direction;
15276 self->a = self->base = self->link->base;
15278 if(self->direction)
15279 self->x = self->link->x - self->modeldata.grabdistance;
15280 else
15281 self->x = self->link->x + self->modeldata.grabdistance;
15283 ent_set_anim(self, ANI_GRAB, 0);
15284 set_pain(self->link, -1, 0);
15285 update_frame(self, self->animation->numframes - 1);
15286 update_frame(self->link, self->link->animation->numframes - 1);
15287 self->takeaction = common_grab;
15288 self->link->takeaction = common_grabbed;
15289 return;
15294 void common_prejump() {
15295 if(self->animating)
15296 return;
15297 dojump(self->jumpv, self->jumpx, self->jumpz, self->jumpid);
15300 void tryjump(float jumpv, float jumpx, float jumpz, int jumpid) {
15301 self->jumpv = jumpv;
15302 self->jumpx = jumpx;
15303 self->jumpz = jumpz;
15304 self->jumpid = jumpid;
15305 if(validanim(self, ANI_JUMPDELAY)) {
15306 ent_set_anim(self, ANI_JUMPDELAY, 0);
15307 self->xdir = self->zdir = 0;
15309 self->idling = 0;
15310 self->takeaction = common_prejump;
15311 } else {
15312 dojump(jumpv, jumpx, jumpz, jumpid);
15317 void dojump(float jumpv, float jumpx, float jumpz, int jumpid) {
15318 entity *dust;
15320 sound_play_sample(samples.jump, 0, savedata.effectvol, savedata.effectvol, 100);
15322 //Spawn jumpstart dust.
15323 if(self->modeldata.dust[2] >= 0) {
15324 dust = spawn(self->x, self->z, self->a, self->direction, NULL, self->modeldata.dust[2], NULL);
15325 dust->base = self->a;
15326 dust->autokill = 1;
15327 execute_onspawn_script(dust);
15330 set_jumping(self);
15331 ent_set_anim(self, jumpid, 0);
15333 toss(self, jumpv);
15335 if(self->direction == 0)
15336 self->xdir = -jumpx;
15337 else
15338 self->xdir = jumpx;
15340 self->zdir = jumpz;
15342 self->takeaction = common_jump;
15345 // make a function so enemies can use
15346 int check_costmove(int s, int fs) {
15347 if(((fs == 1 && level->nospecial < 2) || (fs == 0 && level->nospecial == 0)
15348 || (fs == 0 && level->nospecial == 3)) && (check_energy(0, s) || check_energy(1, s))) {
15349 if(!nocost && !healthcheat) {
15350 if(check_energy(1, s))
15351 self->mp -= self->modeldata.animation[s]->energycost[0];
15352 else
15353 self->health -= self->modeldata.animation[s]->energycost[0];
15356 self->xdir = self->zdir = 0;
15357 set_attacking(self);
15358 self->inpain = 0;
15359 memset(self->combostep, 0, sizeof(int) * 5);
15360 ent_unlink(self);
15361 self->movestep = 0;
15362 ent_set_anim(self, s, 0);
15363 self->takeaction = common_attack_proc;
15364 return 1;
15366 return 0;
15370 // Function to check custom combos. If movestep is 0, means ready to check to see if the second step in the combo is
15371 // valid. If so, returns 1, setting each valid combo in the list so far to 1, otherwise 0. If movestep is > 0, means
15372 // ready to check the action button step of the combo. Loops through the "valid combo" list and sees if the action
15373 // button step is valid, returning 1 if true, otherwise 0.
15374 int check_combo(int m) { // New function to check combos to make sure they are valid
15375 int i;
15376 int found = 0; // Default not found unless overridden by finding a valid combo
15377 int value = self->animation->cancel; // OX. If cancel is enabled , we will be checking MAX_SPECIAL_INPUTS-4, -5, -6 instead.
15379 // check one-step freespecial
15380 for(i = 0; i < self->modeldata.specials_loaded; i++) {
15381 if(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 3] == 1 && self->modeldata.special[i][0] == m
15382 && value == 0) {
15383 if(check_costmove(self->modeldata.special[i][MAX_SPECIAL_INPUTS - (2 + value)], 1)) {
15384 self->modeldata.valid_special = i;
15385 return 1; // Valid combo found, go ahead and return
15386 } else
15387 return 0; // Found, but cost more health than the player had
15388 } else if(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 6] == 1 && self->modeldata.special[i][0] == m
15389 && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 10] <= self->animation->animhits
15390 && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 7] <= self->animpos
15391 && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 8] >= self->animpos
15392 && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 9] == self->animnum) {
15393 if(check_costmove(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 5], 1)) {
15394 self->modeldata.valid_special = i;
15395 return 1; // Valid combo found, go ahead and return
15396 } else
15397 return 0; // Found, but cost more health than the player had
15399 } // end of for
15401 if(borTime > self->movetime)
15402 return 0; // Too much time passed so return 0
15404 if(m == FLAG_FORWARD || m == FLAG_BACKWARD || m == FLAG_MOVEUP || m == FLAG_MOVEDOWN) { // direction keys
15405 for(i = 0; i < self->modeldata.specials_loaded; i++) {
15406 if(self->modeldata.special[i][MAX_SPECIAL_INPUTS - (3 + value)] > self->movestep &&
15407 self->modeldata.special[i][(int) self->movestep] == self->lastmove &&
15408 self->modeldata.special[i][(int) self->movestep + 1] == m) {
15410 self->modeldata.special[i][MAX_SPECIAL_INPUTS - (1 + value)] = 1; // Marks all valid directional combos with a 1
15411 found = 1; // There is at least 1 valid combo, so return found
15412 } else
15413 self->modeldata.special[i][MAX_SPECIAL_INPUTS - (1 + value)] = 0; // Marks all invalid directional combos with a 0
15414 } //end of for
15416 return found; // Returns 1 if found, otherwise returns 0
15417 } else // action buttons
15419 for(i = 0; i < self->modeldata.specials_loaded; i++) {
15420 if(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 1] && self->modeldata.special[i][self->movestep + 1] == m && value == 0) { // Checks only valid directional combos to see if the action button matches
15421 if(check_costmove(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 2], 1)) {
15422 self->modeldata.valid_special = i; // Says which one is valid and returns that it was found
15423 return 1; // Valid combo found, go ahead and return
15424 } else
15425 return 0; // Found, but cost more health than the player had
15426 } else if(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 4] && self->modeldata.special[i][self->movestep + 1] == m && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 10] <= self->animation->animhits && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 7] <= self->animpos && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 8] >= self->animpos && self->modeldata.special[i][MAX_SPECIAL_INPUTS - 9] == self->animnum) { // Checks only valid directional combos to see if the action button matches
15427 if(check_costmove(self->modeldata.special[i][MAX_SPECIAL_INPUTS - 5], 1)) {
15428 self->modeldata.valid_special = i; // Says which one is valid and returns that it was found
15429 return 1; // Valid combo found, go ahead and return
15430 } else
15431 return 0; // Found, but cost more health than the player had
15433 } // end of for
15435 return 0; // No valid combos found, return 0
15440 // Function that causes the player to continue to move up or down until the animation has finished playing
15441 void common_dodge() // New function so players can dodge with up up or down down
15443 if(self->animating) // Continues to move as long as the player is animating
15445 if(self->zdir < 0)
15446 self->zdir = -self->modeldata.speed * 1.75;
15447 else
15448 self->zdir = self->modeldata.speed * 1.75;
15449 self->xdir = 0;
15450 } else // Once done animating, returns to thinking
15452 self->xdir = self->zdir = 0;
15453 set_idle(self);
15454 self->takeaction = NULL;
15460 // Function created to combine the action taken if either picking up an item, or running into an item that is a
15461 // SUBTYPE_TOUCH, executing the appropriate action based on which type of item is picked up
15462 void didfind_item(entity * other) { // Function that takes care of items when picked up
15463 set_opponent(self, other);
15465 //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
15466 if(other->modeldata.reload) {
15467 if(self->weapent && self->weapent->modeldata.typeshot) {
15468 self->weapent->modeldata.shootnum += other->modeldata.reload;
15469 if(self->weapent->modeldata.shootnum > self->weapent->modeldata.shootnum)
15470 self->weapent->modeldata.shootnum = self->weapent->modeldata.shootnum;
15471 sound_play_sample(samples.get, 0, savedata.effectvol, savedata.effectvol, 100);
15472 } else {
15473 addscore(self->playerindex, other->modeldata.score);
15474 sound_play_sample(samples.get2, 0, savedata.effectvol, savedata.effectvol, 100);
15477 //end of weapons items section
15478 else if(other->modeldata.score) {
15479 addscore(self->playerindex, other->modeldata.score);
15480 sound_play_sample(samples.get2, 0, savedata.effectvol, savedata.effectvol, 100);
15481 } else if(other->health) {
15482 self->health += other->health;
15484 if(self->health > self->modeldata.health)
15485 self->health = self->modeldata.health;
15487 other->health = 0;
15488 sound_play_sample(samples.get, 0, savedata.effectvol, savedata.effectvol, 100);
15489 } else if(other->modeldata.mp) {
15490 self->mp += other->modeldata.mp;
15492 if(self->mp > self->modeldata.mp)
15493 self->mp = self->modeldata.mp;
15495 other->mp = 0;
15496 sound_play_sample(samples.get, 0, savedata.effectvol, savedata.effectvol, 100);
15497 } else if(stricmp(other->modeldata.name, "Time") == 0) {
15498 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
15500 sound_play_sample(samples.get2, 0, savedata.effectvol, savedata.effectvol, 100);
15501 } else if(other->modeldata.makeinv) { // Mar 2, 2005 - New item makes player invincible
15502 self->invincible = 1;
15503 self->invinctime = borTime + ABS(other->modeldata.makeinv);
15504 self->blink = (other->modeldata.makeinv > 0);
15505 sound_play_sample(samples.get2, 0, savedata.effectvol, savedata.effectvol, 100);
15506 } else if(other->modeldata.smartbomb) { // Damages everything on the screen
15507 smart_bomb(self, other->modeldata.smartbomb);
15508 sound_play_sample(samples.get2, 0, savedata.effectvol, savedata.effectvol, 100);
15509 } else if(other->modeldata.subtype == SUBTYPE_WEAPON) {
15510 dropweapon(0);
15511 self->weapent = other;
15512 set_weapon(self, other->modeldata.weapnum, 0);
15514 if(self->modeldata.animal) // UTunnels: well, ride, not get. :)
15516 self->direction = other->direction;
15517 self->x = other->x;
15518 self->z = other->z;
15521 if(!other->modeldata.typeshot && self->modeldata.typeshot)
15522 other->modeldata.typeshot = 1;
15524 sound_play_sample(samples.get, 0, savedata.effectvol, savedata.effectvol, 100);
15525 } else if(other->modeldata.subtype == SUBTYPE_PROJECTILE) {
15526 dropweapon(0);
15527 self->weapent = other;
15529 sound_play_sample(samples.get, 0, savedata.effectvol, savedata.effectvol, 100);
15530 } else if(other->modeldata.credit) {
15531 if(!noshare)
15532 credits++;
15533 else
15534 player[(int) self->playerindex].credits++;
15536 sound_play_sample(samples.oneup, 0, savedata.effectvol, savedata.effectvol, 100);
15537 } else {
15538 // Must be a 1up then.
15539 player[(int) self->playerindex].lives++;
15541 sound_play_sample(samples.oneup, 0, savedata.effectvol, savedata.effectvol, 100);
15544 if(other->modeldata.subtype != SUBTYPE_WEAPON && other->modeldata.subtype != SUBTYPE_PROJECTILE) {
15545 other->takeaction = suicide;
15546 if(!other->modeldata.instantitemdeath)
15547 other->nextthink = borTime + GAME_SPEED * 3;
15549 other->z = 100000;
15552 void player_fall_check() {
15553 if(autoland != 2
15554 && (player[(int) self->playerindex].keys & (FLAG_MOVEUP | FLAG_JUMP)) == (FLAG_MOVEUP | FLAG_JUMP)) {
15555 player[(int) self->playerindex].playkeys ^= (FLAG_MOVEUP | FLAG_JUMP);
15556 self->damage_on_landing = -2; // mark it, so we will play land animation when hit the ground
15560 void player_grab_check() {
15561 entity *other = self->link;
15563 if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking))
15564 return;
15566 if(self->base != other->base) { // Change this from ->a to ->base
15567 ent_unlink(self);
15568 set_idle(self);
15569 self->takeaction = NULL;
15570 return;
15572 // OX cancel checking.
15573 if(self->animation->cancel == 3) {
15574 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) && check_combo(FLAG_ATTACK)) {
15575 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15576 ent_unlink(self);
15577 self->attacking = 1;
15578 self->takeaction = common_attack_proc;
15579 return;
15581 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK2) && check_combo(FLAG_ATTACK2)) {
15582 player[(int) self->playerindex].playkeys -= FLAG_ATTACK2;
15583 ent_unlink(self);
15584 self->attacking = 1;
15585 self->takeaction = common_attack_proc;
15586 return;
15588 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK3) && check_combo(FLAG_ATTACK3)) {
15589 player[(int) self->playerindex].playkeys -= FLAG_ATTACK3;
15590 ent_unlink(self);
15591 self->attacking = 1;
15592 self->takeaction = common_attack_proc;
15593 return;
15595 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK4) && check_combo(FLAG_ATTACK4)) {
15596 player[(int) self->playerindex].playkeys -= FLAG_ATTACK4;
15597 ent_unlink(self);
15598 self->attacking = 1;
15599 self->takeaction = common_attack_proc;
15600 return;
15602 if((player[(int) self->playerindex].playkeys & FLAG_JUMP) && check_combo(FLAG_JUMP)) {
15603 player[(int) self->playerindex].playkeys -= FLAG_JUMP;
15604 ent_unlink(self);
15605 self->attacking = 1;
15606 self->takeaction = common_attack_proc;
15607 return;
15609 if((player[(int) self->playerindex].playkeys & FLAG_SPECIAL) && check_combo(FLAG_SPECIAL)) {
15610 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
15611 ent_unlink(self);
15612 self->attacking = 1;
15613 self->takeaction = common_attack_proc;
15614 return;
15617 // End cancel checking
15619 if(player_check_special())
15620 return;
15622 if(!nolost && self->modeldata.weaploss[0] <= 0)
15623 dropweapon(1);
15625 // grabturn code
15626 if(self->animation == self->modeldata.animation[ANI_GRABTURN]) {
15627 // still turning? don't bother with anything else
15628 if(self->animating)
15629 return;
15631 // done turning? switch directions and return to grab animation
15632 else {
15633 if(self->direction) {
15634 self->direction = 0;
15635 other->direction = 1;
15636 } else {
15637 self->direction = 1;
15638 other->direction = 0;
15640 ent_set_anim(self, ANI_GRAB, 0);
15641 set_pain(other, -1, 0);
15642 update_frame(self, self->animation->numframes - 1);
15643 update_frame(other, other->animation->numframes - 1);
15644 other->x = self->x + (((self->direction * 2) - 1) * self->modeldata.grabdistance);
15648 self->attacking = 0; //for checking
15649 self->grabwalking = 0;
15650 if(self->direction ?
15651 (player[(int) self->playerindex].keys & FLAG_MOVELEFT) :
15652 (player[(int) self->playerindex].keys & FLAG_MOVERIGHT)) {
15653 // initiating grabturn
15654 if(self->modeldata.grabturn) {
15655 // start animation if it exists...
15656 if(validanim(self, ANI_GRABTURN)) {
15657 ent_set_anim(self, ANI_GRABTURN, 0);
15658 if(validanim(other, ANI_GRABBEDTURN))
15659 ent_set_anim(other, ANI_GRABBEDTURN, 0);
15660 else if(validanim(other, ANI_GRABBED))
15661 ent_set_anim(other, ANI_GRABBED, 0);
15662 else
15663 ent_set_anim(other, ANI_PAIN, 0);
15664 other->xdir = other->zdir = self->xdir = self->zdir = 0;
15665 other->x = self->x;
15666 return;
15668 // otherwise, just turn around
15669 else {
15670 if(self->direction) {
15671 self->direction = 0;
15672 other->direction = 1;
15673 } else {
15674 self->direction = 1;
15675 other->direction = 0;
15677 ent_set_anim(self, ANI_GRAB, 0);
15678 set_pain(other, -1, 0);
15679 update_frame(self, self->animation->numframes - 1);
15680 update_frame(other, other->animation->numframes - 1);
15681 other->x = self->x + (((self->direction * 2) - 1) * self->modeldata.grabdistance);
15683 } else if(!validanim(self, ANI_GRABWALK) && borTime > self->releasetime) {
15684 // Release
15685 ent_unlink(self);
15686 set_idle(self);
15687 self->takeaction = NULL;
15688 return;
15690 } else
15691 self->releasetime = borTime + (GAME_SPEED / 2);
15693 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) &&
15694 (self->direction ?
15695 (player[(int) self->playerindex].keys & FLAG_MOVELEFT) :
15696 (player[(int) self->playerindex].keys & FLAG_MOVERIGHT))) {
15697 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15698 if(validanim(self, ANI_GRABBACKWARD))
15699 dograbattack(4);
15700 else if(validanim(self, ANI_THROW)) {
15701 if(self->modeldata.throwframewait >= 0)
15702 doprethrow();
15703 else
15704 dothrow();
15705 } else
15706 dograbattack(0);
15708 // grab forward
15709 else if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) &&
15710 validanim(self, ANI_GRABFORWARD) &&
15711 (!self->direction ?
15712 (player[(int) self->playerindex].keys & FLAG_MOVELEFT) :
15713 (player[(int) self->playerindex].keys & FLAG_MOVERIGHT))) {
15714 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15715 dograbattack(1);
15717 // grab up
15718 else if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) &&
15719 validanim(self, ANI_GRABUP) && (player[(int) self->playerindex].keys & FLAG_MOVEUP)) {
15720 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15721 dograbattack(2);
15723 // grab down
15724 else if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) &&
15725 validanim(self, ANI_GRABDOWN) && (player[(int) self->playerindex].keys & FLAG_MOVEDOWN)) {
15726 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15727 dograbattack(3);
15729 // normal grab attack
15730 else if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) && validanim(self, ANI_GRABATTACK)) {
15731 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15732 dograbattack(0);
15734 // Vaulting.
15735 else if((player[(int) self->playerindex].playkeys & FLAG_JUMP) && validanim(self, ANI_VAULT)) {
15736 player[(int) self->playerindex].playkeys -= FLAG_JUMP;
15737 dovault();
15739 // grab attack finisher
15740 else if(player[(int) self->playerindex].playkeys & (FLAG_JUMP | FLAG_ATTACK)) {
15741 player[(int) self->playerindex].playkeys -=
15742 player[(int) self->playerindex].playkeys & (FLAG_JUMP | FLAG_ATTACK);
15744 // Perform final blow
15745 if(validanim(self, ANI_GRABATTACK2) || validanim(self, ANI_ATTACK3))
15746 dograbattack(-1);
15747 else {
15748 self->attacking = 1;
15749 memset(self->combostep, 0, sizeof(int) * 5);
15750 self->takeaction = common_grabattack;
15751 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, 0, ANI_JUMP);
15754 // grab walk code
15755 else if(validanim(self, ANI_GRABWALK) // check if grabwalk animation exists
15756 // if entity is still animating anything besides a grabwalk variant, don't let them move
15757 && (!self->animating || self->animation == self->modeldata.animation[ANI_GRABWALK]
15758 || self->animation == self->modeldata.animation[ANI_GRABWALKUP]
15759 || self->animation == self->modeldata.animation[ANI_GRABWALKDOWN]
15760 || self->animation == self->modeldata.animation[ANI_GRABBACKWALK])) {
15762 // z axis movement
15763 if(PLAYER_MIN_Z != PLAYER_MAX_Z) {
15764 if(player[(int) self->playerindex].keys & FLAG_MOVEUP) {
15765 if(self->modeldata.grabwalkspeed)
15766 self->zdir = -self->modeldata.grabwalkspeed / 2;
15767 else
15768 self->zdir = -self->modeldata.speed / 2;
15769 } else if(player[(int) self->playerindex].keys & FLAG_MOVEDOWN) {
15770 if(self->modeldata.grabwalkspeed)
15771 self->zdir = self->modeldata.grabwalkspeed / 2;
15772 else
15773 self->zdir = self->modeldata.speed / 2;
15774 } else if(!(player[(int) self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
15775 self->zdir = 0;
15777 // x axis movement
15778 if(player[(int) self->playerindex].keys & FLAG_MOVELEFT) {
15779 if(self->modeldata.grabwalkspeed)
15780 self->xdir = -self->modeldata.grabwalkspeed;
15781 else
15782 self->xdir = -self->modeldata.speed;
15785 else if(player[(int) self->playerindex].keys & FLAG_MOVERIGHT) {
15786 if(self->modeldata.grabwalkspeed)
15787 self->xdir = self->modeldata.grabwalkspeed;
15788 else
15789 self->xdir = self->modeldata.speed;
15790 } else
15791 if(!
15792 ((player[(int) self->playerindex].keys & FLAG_MOVELEFT)
15793 || (player[(int) self->playerindex].keys & FLAG_MOVERIGHT)))
15794 self->xdir = 0;
15796 // setting the animations based on the velocity set above
15797 if(self->xdir || self->zdir) {
15798 if(((self->xdir > 0 && !self->direction) || (self->xdir < 0 && self->direction))
15799 && validanim(self, ANI_GRABBACKWALK))
15800 ent_set_anim(self, ANI_GRABBACKWALK, 0);
15801 else if(self->zdir < 0 && validanim(self, ANI_GRABWALKUP))
15802 ent_set_anim(self, ANI_GRABWALKUP, 0);
15803 else if(self->zdir > 0 && validanim(self, ANI_GRABWALKDOWN))
15804 ent_set_anim(self, ANI_GRABWALKDOWN, 0);
15805 else
15806 ent_set_anim(self, ANI_GRABWALK, 0);
15807 if(self->animation == self->modeldata.animation[ANI_GRABWALKUP]
15808 && validanim(other, ANI_GRABBEDWALKUP))
15809 ent_set_anim(other, ANI_GRABBEDWALKUP, 0);
15810 else if(self->animation == self->modeldata.animation[ANI_GRABWALKDOWN]
15811 && validanim(other, ANI_GRABBEDWALKDOWN))
15812 ent_set_anim(other, ANI_GRABBEDWALKDOWN, 0);
15813 else if(self->animation == self->modeldata.animation[ANI_GRABBACKWALK]
15814 && validanim(other, ANI_GRABBEDBACKWALK))
15815 ent_set_anim(other, ANI_GRABBEDBACKWALK, 0);
15816 else if(validanim(other, ANI_GRABBEDWALK))
15817 ent_set_anim(other, ANI_GRABBEDWALK, 0);
15818 else if(validanim(other, ANI_GRABBED))
15819 ent_set_anim(other, ANI_GRABBED, 0);
15820 else
15821 ent_set_anim(other, ANI_PAIN, 0);
15822 } else {
15823 ent_set_anim(self, ANI_GRAB, 0);
15824 if(validanim(other, ANI_GRABBED))
15825 ent_set_anim(other, ANI_GRABBED, 0);
15826 else
15827 ent_set_anim(other, ANI_PAIN, 0);
15829 // use check_link_move to set velocity, don't change it here
15830 other->zdir = other->xdir = 0;
15831 self->grabwalking = 1;
15833 player_preinput();
15835 if(self->attacking)
15836 self->releasetime = borTime + (GAME_SPEED / 2); // reset releasetime when do attacks
15840 void player_jump_check() {
15841 int candospecial = 0;
15842 if(!noaircancel || !self->animating || self->animnum == self->jumpid) {
15843 //air special, copied and changed from Fugue's code
15844 if((!level->nospecial || level->nospecial == 3)
15845 && player[(int) self->playerindex].playkeys & FLAG_SPECIAL) {
15847 if(validanim(self, ANI_JUMPSPECIAL)) {
15848 if(check_energy(1, ANI_JUMPSPECIAL)) {
15849 if(!healthcheat)
15850 self->mp -= self->modeldata.animation[ANI_JUMPSPECIAL]->energycost[0];
15851 candospecial = 1;
15852 } else if(check_energy(0, ANI_JUMPSPECIAL)) {
15853 if(!healthcheat)
15854 self->health -=
15855 self->modeldata.animation[ANI_JUMPSPECIAL]->energycost[0];
15856 candospecial = 1;
15857 } else if(validanim(self, ANI_JUMPCANT)) {
15858 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
15859 ent_set_anim(self, ANI_JUMPCANT, 0);
15860 self->tossv = 0;
15863 if(candospecial) {
15864 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
15865 ent_set_anim(self, ANI_JUMPSPECIAL, 0);
15866 self->attacking = 1;
15867 self->xdir = self->zdir = 0; // Kill movement when the special starts
15868 self->tossv = 0;
15871 } //end of jumpspecial
15873 //jumpattacks, up down forward normal....we don't check energy cost
15874 else if(player[(int) self->playerindex].playkeys & FLAG_ATTACK) {
15875 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
15876 self->attacking = 1;
15877 //OX cancel checking of attack button for compatibility
15878 if(self->animation->cancel == 3 && check_combo(FLAG_ATTACK)) {
15879 //player[(int)self->playerindex].playkeys -= FLAG_ATTACK;
15880 self->jumping = 1;
15881 self->takeaction = common_jump;
15882 return;
15884 // End cancel check.
15886 else if((player[(int) self->playerindex].keys & FLAG_MOVEDOWN)
15887 && validanim(self, ANI_JUMPATTACK2))
15888 ent_set_anim(self, ANI_JUMPATTACK2, 0);
15889 else if((player[(int) self->playerindex].keys & FLAG_MOVEUP)
15890 && validanim(self, ANI_JUMPATTACK3))
15891 ent_set_anim(self, ANI_JUMPATTACK3, 0);
15892 else if(self->running && validanim(self, ANI_RUNJUMPATTACK))
15893 ent_set_anim(self, ANI_RUNJUMPATTACK, 0); // Added so an extra strong jump attack can be executed
15894 else if(self->xdir != 0 && validanim(self, ANI_JUMPFORWARD))
15895 ent_set_anim(self, ANI_JUMPFORWARD, 0); // If moving and set, do this attack
15896 else if(validanim(self, ANI_JUMPATTACK))
15897 ent_set_anim(self, ANI_JUMPATTACK, 0);
15898 } //end of jumpattack
15900 if(self->modeldata.jumpmovex & 1) //flip?
15902 if((player[(int) self->playerindex].keys & FLAG_MOVELEFT))
15903 self->direction = 0;
15904 else if((player[(int) self->playerindex].keys & FLAG_MOVERIGHT))
15905 self->direction = 1;
15907 if(self->modeldata.jumpmovex & 2) //move?
15909 if(((player[(int) self->playerindex].keys & FLAG_MOVELEFT) && self->xdir > 0) ||
15910 ((player[(int) self->playerindex].keys & FLAG_MOVERIGHT) && self->xdir < 0))
15911 self->xdir = -self->xdir;
15913 if(self->modeldata.jumpmovex & 4) //Move x if vertical jump?
15915 if(((player[(int) self->playerindex].keys & FLAG_MOVELEFT) && self->xdir > 0) ||
15916 ((player[(int) self->playerindex].keys & FLAG_MOVERIGHT) && self->xdir < 0))
15917 self->xdir = -self->xdir;
15919 if((player[(int) self->playerindex].keys & FLAG_MOVELEFT) && (!self->xdir)) {
15920 self->xdir -= self->modeldata.speed;
15921 } else if((player[(int) self->playerindex].keys & FLAG_MOVERIGHT) && (!self->xdir)) {
15922 self->xdir = self->modeldata.speed;
15925 if(self->modeldata.jumpmovez & 2) //z move?
15927 if(((player[(int) self->playerindex].keys & FLAG_MOVEUP) && self->zdir > 0) ||
15928 ((player[(int) self->playerindex].keys & FLAG_MOVEDOWN) && self->zdir < 0))
15929 self->zdir = -self->zdir;
15931 if(self->modeldata.jumpmovez & 4) //Move z if vertical jump?
15933 if((player[(int) self->playerindex].keys & FLAG_MOVELEFT))
15934 self->direction = 0;
15935 else if((player[(int) self->playerindex].keys & FLAG_MOVERIGHT))
15936 self->direction = 1;
15938 if(((player[(int) self->playerindex].keys & FLAG_MOVEUP) && self->zdir > 0) ||
15939 ((player[(int) self->playerindex].keys & FLAG_MOVEDOWN) && self->zdir < 0))
15940 self->zdir = -self->zdir;
15942 if((player[(int) self->playerindex].keys & FLAG_MOVEUP) && (!self->zdir)) {
15943 self->zdir -= (0.5 * self->modeldata.speed);
15944 } else if((player[(int) self->playerindex].keys & FLAG_MOVEDOWN) && (!self->zdir)) {
15945 self->zdir = (0.5 * self->modeldata.speed);
15948 //OX Rest of cancel checking
15950 if(self->animation->cancel == 3) {
15951 if((player[(int) self->playerindex].playkeys & FLAG_JUMP) && check_combo(FLAG_JUMP)) {
15952 player[(int) self->playerindex].playkeys -= FLAG_JUMP;
15953 self->jumping = 1;
15954 self->takeaction = common_jump;
15955 return;
15957 if((player[(int) self->playerindex].playkeys & FLAG_SPECIAL) && check_combo(FLAG_SPECIAL)) {
15958 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
15959 self->jumping = 1;
15960 self->takeaction = common_jump;
15961 return;
15963 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK2) && check_combo(FLAG_ATTACK2)) {
15964 player[(int) self->playerindex].playkeys -= FLAG_ATTACK2;
15965 //set_jumping(self);
15966 self->jumping = 1;
15967 self->takeaction = common_jump;
15968 return;
15970 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK3) && check_combo(FLAG_ATTACK3)) {
15971 player[(int) self->playerindex].playkeys -= FLAG_ATTACK3;
15972 self->jumping = 1;
15973 self->takeaction = common_jump;
15974 return;
15976 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK4) && check_combo(FLAG_ATTACK4)) {
15977 player[(int) self->playerindex].playkeys -= FLAG_ATTACK4;
15978 self->jumping = 1;
15979 self->takeaction = common_jump;
15980 return;
15983 // End cancel checking.
15985 player_preinput();
15988 void player_pain_check() {
15989 if(player_check_special())
15990 self->inpain = 0;
15991 player_preinput();
15994 // check riseattack input up+attack
15995 void player_lie_check() {
15996 if(validanim(self, ANI_RISEATTACK) &&
15997 (player[(int) self->playerindex].playkeys & FLAG_ATTACK) &&
15998 (player[(int) self->playerindex].keys & FLAG_MOVEUP) && (self->health > 0 && borTime > self->staydown[2])) {
15999 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
16000 if((player[(int) self->playerindex].keys & FLAG_MOVELEFT)) {
16001 self->direction = 0;
16003 if((player[(int) self->playerindex].keys & FLAG_MOVERIGHT)) {
16004 self->direction = 1;
16006 self->stalltime = 0;
16007 set_riseattack(self, self->damagetype, 0);
16009 player_preinput();
16012 void player_charge_check() {
16013 if(!((player[(int) self->playerindex].keys & FLAG_JUMP) &&
16014 (player[(int) self->playerindex].keys & FLAG_SPECIAL))) {
16015 self->charging = 0;
16016 set_idle(self);
16017 self->takeaction = NULL;
16021 void player_preinput() {
16022 float altdiff; // Used to check that
16023 int notinair; // entity is not in the air
16024 int i;
16025 static const int check_flags[] = {
16026 FLAG_ATTACK,
16027 FLAG_ATTACK2,
16028 FLAG_ATTACK3,
16029 FLAG_ATTACK4,
16030 FLAG_JUMP,
16031 FLAG_SPECIAL,
16033 static const int max_check_flags = 6;
16035 static const int check_flags2[] = {
16036 FLAG_MOVEUP,
16037 FLAG_MOVEDOWN,
16038 FLAG_MOVELEFT,
16039 FLAG_MOVERIGHT,
16041 static const int max_check_flags2 = 4;
16042 for(i = 0 ; i < max_check_flags2; i++) {
16043 if(player[(int) self->playerindex].playkeys & check_flags2[i]) {
16044 player[(int) self->playerindex].playkeys -= check_flags2[i];
16045 self->lastdir = check_flags2[i] == FLAG_MOVEUP || check_flags2[i] == FLAG_MOVEDOWN ? 0 : check_flags2[i];
16047 if(self->lastdir) {
16048 if(!self->direction && check_combo(FLAG_FORWARD))
16049 ++self->movestep; // Check direction to distinguish forward/backward movements
16050 else if(self->direction && check_combo(FLAG_BACKWARD))
16051 ++self->movestep; // Check direction to distinguish forward/backward movements
16052 else
16053 self->movestep = 0;
16055 if(self->direction)
16056 self->lastmove = FLAG_BACKWARD;
16057 else
16058 self->lastmove = FLAG_FORWARD;
16060 } else {
16061 if(check_combo(check_flags2[i]))
16062 ++self->movestep; // Check the combo and increase movestep if valid
16063 else
16064 self->movestep = 0;
16066 self->lastmove = check_flags2[i];
16068 self->movetime = borTime + (GAME_SPEED / 4);
16069 return;
16073 // OX Cancel checking.
16075 altdiff = diff(self->a, self->base);
16076 notinair = (self->landed_on_platform ? altdiff < 5 : altdiff < 2);
16078 if(self->attacking && self->animation->cancel == 3 && notinair) {
16079 for(i = 0; i < max_check_flags; i++)
16080 if((player[(int) self->playerindex].playkeys & check_flags[i]) && check_combo(check_flags[i])) {
16081 player[(int) self->playerindex].playkeys -= check_flags[i];
16082 self->attacking = 1;
16083 self->takeaction = common_attack_proc;
16084 return;
16087 // End cancel checking.
16090 void player_think() {
16091 int action = 0; // 1=walking, 2=up, 3=down, 4=running
16092 int bkwalk = 0; //backwalk
16093 entity *other = NULL;
16094 float altdiff;
16095 float seta;
16096 int notinair;
16098 if(player[(int) self->playerindex].ent != self || self->dead)
16099 return;
16101 seta = (float) (self->animation->seta ? self->animation->seta[self->animpos] : -1);
16102 // check endlevel item
16103 if((other = find_ent_here(self, self->x, self->z, TYPE_ENDLEVEL)) && self->a -
16104 (seta >= 0 ? seta : 0) == other->a) {
16105 if(!reached[0] && !reached[1] && !reached[2] && !reached[3])
16106 addscore(self->playerindex, other->modeldata.score);
16107 reached[(int) self->playerindex] = 1;
16109 if(!other->modeldata.subtype || (other->modeldata.subtype == SUBTYPE_BOTH &&
16110 (reached[0] + reached[1] + reached[2] + reached[3]) >=
16111 (count_ents(TYPE_PLAYER)))) {
16112 level_completed = 1;
16114 if(other->modeldata.branch)
16115 strncpy(branch_name, other->modeldata.branch, MAX_NAME_LEN); //now, you can branch to another level
16116 return;
16120 if(borTime > player[(int) self->playerindex].ent->rushtime) {
16121 player[(int) self->playerindex].ent->rush[0] = 0;
16122 player[(int) self->playerindex].ent->rushtime = 0;
16125 if(self->charging) {
16126 player_charge_check();
16127 return;
16130 if(self->inpain || (self->link && !self->grabbing)) {
16131 player_pain_check();
16132 return;
16134 // falling? check for landing
16135 if(self->projectile == 2) {
16136 player_fall_check();
16137 return;
16139 // grab section, dont move if still animating
16140 if(self->grabbing && !self->attacking && self->takeaction != common_throw_wait) {
16141 player_grab_check();
16142 return;
16144 // jump section
16145 if(self->jumping) {
16146 player_jump_check();
16147 return;
16150 if(self->drop && self->a == self->base && !self->tossv) {
16151 player_lie_check();
16152 return;
16155 // cant do anything if busy
16156 if(!self->idling && !(self->animation->idle && self->animation->idle[self->animpos])) {
16157 player_preinput();
16158 return;
16160 // Check if entity is under a platform
16161 if(self->modeldata.subject_to_platform > 0 && validanim(self, ANI_DUCK)
16162 && check_platform_between(self->x /*+self->direction*2-1 */ , self->z, self->a,
16163 self->a + self->modeldata.height)
16165 (check_platform_between
16166 (self->x /*+self->direction*2-1 */ , self->z, self->a, self->a + self->animation->height)
16167 || !self->animation->height)) {
16168 ent_set_anim(self, ANI_DUCK, 1);
16169 self->takeaction = common_stuck_underneath;
16170 return;
16173 altdiff = diff(self->a, self->base);
16174 notinair = (self->landed_on_platform ? altdiff < 5 : altdiff < 2);
16176 // Changed the way combos are checked so combos can be customized
16177 if(player[(int) self->playerindex].playkeys & FLAG_MOVEUP) {
16178 player[(int) self->playerindex].playkeys -= FLAG_MOVEUP;
16179 self->lastdir = 0;
16180 if(borTime < self->movetime && self->lastmove == FLAG_MOVEUP && validanim(self, ANI_ATTACKUP) && notinair) { // New u u combo attack
16181 set_attacking(self);
16182 self->combostep[0] = 0;
16183 self->xdir = self->zdir = 0;
16184 ent_set_anim(self, ANI_ATTACKUP, 0);
16185 self->takeaction = common_attack_proc;
16186 return;
16187 } else if(borTime < self->movetime && self->lastmove == FLAG_MOVEUP && validanim(self, ANI_DODGE) && notinair) { // New dodge move like on SOR3
16188 self->combostep[0] = 0;
16189 self->idling = 0;
16190 self->zdir = (float) -0.1;
16191 ent_set_anim(self, ANI_DODGE, 0);
16192 self->takeaction = common_dodge;
16193 return;
16194 } else if(check_combo(FLAG_MOVEUP))
16195 ++self->movestep; // Check the combo and increase movestep if valid
16196 else
16197 self->movestep = 0;
16199 self->lastmove = FLAG_MOVEUP;
16200 self->movetime = borTime + (GAME_SPEED / 4);
16203 if(player[(int) self->playerindex].playkeys & FLAG_MOVEDOWN) {
16204 player[(int) self->playerindex].playkeys -= FLAG_MOVEDOWN;
16205 self->lastdir = 0;
16206 if(self->lastmove == FLAG_MOVEDOWN && validanim(self, ANI_ATTACKDOWN) && borTime < self->movetime && notinair) { // New d d combo attack
16207 set_attacking(self);
16208 self->xdir = self->zdir = 0;
16209 self->combostep[0] = 0;
16210 ent_set_anim(self, ANI_ATTACKDOWN, 0);
16211 self->takeaction = common_attack_proc;
16212 return;
16213 } else if(borTime < self->movetime && self->lastmove == FLAG_MOVEDOWN && validanim(self, ANI_DODGE) && notinair) { // New dodge move like on SOR3
16214 self->combostep[0] = 0;
16215 self->idling = 0;
16216 self->zdir = (float) 0.1; //used for checking
16217 ent_set_anim(self, ANI_DODGE, 0);
16218 self->takeaction = common_dodge;
16219 return;
16220 } else if(check_combo(FLAG_MOVEDOWN))
16221 ++self->movestep; // Check the combo and increase movestep if valid
16222 else
16223 self->movestep = 0;
16225 self->lastmove = FLAG_MOVEDOWN;
16226 self->movetime = borTime + (GAME_SPEED / 4);
16228 // Left/right movement for combos is more complicated because forward/backward is relative to the direction the player is facing
16229 // Checks on current direction have to be made before and after executing the combo to make sure they get the right one
16230 if((player[(int) self->playerindex].playkeys & FLAG_MOVELEFT)) {
16231 player[(int) self->playerindex].playkeys -= FLAG_MOVELEFT;
16232 if(validanim(self, ANI_RUN) && !self->direction && borTime < self->movetime
16233 && self->lastdir == FLAG_MOVELEFT)
16234 self->running = 1; // Player begins to run
16236 else if(validanim(self, ANI_ATTACKFORWARD) && !self->direction && borTime < self->movetime
16237 && self->lastdir == FLAG_MOVELEFT) {
16238 set_attacking(self);
16239 self->xdir = self->zdir = 0;
16240 self->combostep[0] = 0;
16241 ent_set_anim(self, ANI_ATTACKFORWARD, 0);
16242 self->takeaction = common_attack_proc;
16243 return;
16244 } else if(!self->direction && check_combo(FLAG_FORWARD))
16245 ++self->movestep; // Check direction to distinguish forward/backward movements
16246 else if(self->direction && check_combo(FLAG_BACKWARD))
16247 ++self->movestep; // Check direction to distinguish forward/backward movements
16248 else
16249 self->movestep = 0;
16251 if(self->direction)
16252 self->lastmove = FLAG_BACKWARD;
16253 else
16254 self->lastmove = FLAG_FORWARD;
16255 self->lastdir = FLAG_MOVELEFT;
16257 self->movetime = borTime + (GAME_SPEED / 4);
16260 if((player[(int) self->playerindex].playkeys & FLAG_MOVERIGHT)) {
16261 player[(int) self->playerindex].playkeys -= FLAG_MOVERIGHT;
16262 if(validanim(self, ANI_RUN) && self->direction && borTime < self->movetime
16263 && self->lastdir == FLAG_MOVERIGHT)
16264 self->running = 1; // Player begins to run
16266 else if(validanim(self, ANI_ATTACKFORWARD) && self->direction && borTime < self->movetime
16267 && self->lastdir == FLAG_MOVERIGHT) {
16268 set_attacking(self);
16269 self->xdir = self->zdir = 0;
16270 self->combostep[0] = 0;
16271 ent_set_anim(self, ANI_ATTACKFORWARD, 0);
16272 self->takeaction = common_attack_proc;
16273 return;
16274 } else if(!self->direction && check_combo(FLAG_BACKWARD))
16275 ++self->movestep; // Check direction to distinguish forward/backward movements
16276 else if(self->direction && check_combo(FLAG_FORWARD))
16277 ++self->movestep; // Check direction to distinguish forward/backward movements
16278 else
16279 self->movestep = 0;
16281 if(!self->direction)
16282 self->lastmove = FLAG_BACKWARD;
16283 else
16284 self->lastmove = FLAG_FORWARD;
16285 self->lastdir = FLAG_MOVERIGHT;
16287 self->movetime = borTime + (GAME_SPEED / 4);
16290 if(!ajspecial && (player[(int) self->playerindex].playkeys & FLAG_JUMP) && validanim(self, ANI_ATTACKBOTH)) {
16291 if((player[(int) self->playerindex].keys & FLAG_ATTACK) && notinair) {
16292 player[(int) self->playerindex].playkeys -= FLAG_JUMP;
16293 set_attacking(self);
16294 self->xdir = self->zdir = 0;
16295 self->combostep[0] = 0;
16296 self->movestep = 0;
16297 self->stalltime = 0; // If attack is pressed, holding down attack to execute attack3 is no longer valid
16298 ent_set_anim(self, ANI_ATTACKBOTH, 0);
16299 self->takeaction = common_attack_proc;
16300 return;
16304 if((player[(int) self->playerindex].playkeys & FLAG_JUMP) && validanim(self, ANI_CHARGE)) {
16305 if((player[(int) self->playerindex].playkeys & FLAG_SPECIAL) && notinair) {
16306 self->combostep[0] = 0;
16307 self->movestep = 0;
16308 self->xdir = self->zdir = 0;
16309 self->stalltime = 0;
16310 set_charging(self);
16311 ent_set_anim(self, ANI_CHARGE, 0);
16312 self->takeaction = common_charge;
16313 return;
16317 if(player[(int) self->playerindex].playkeys & FLAG_SPECIAL) // The special button can now be used for freespecials
16319 if(validanim(self, ANI_SPECIAL2) && notinair &&
16320 (!self->direction ?
16321 (player[(int) self->playerindex].keys & FLAG_MOVELEFT) :
16322 (player[(int) self->playerindex].keys & FLAG_MOVERIGHT))) {
16323 if(check_costmove(ANI_SPECIAL2, 0)) {
16324 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
16325 return;
16329 if(check_combo(FLAG_SPECIAL)) {
16330 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
16331 return;
16334 if(validanim(self, ANI_BLOCK) && !self->modeldata.holdblock && notinair) // New block code for players
16336 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
16337 self->xdir = self->zdir = 0;
16338 set_blocking(self);
16339 self->combostep[0] = 0;
16340 self->movestep = 0;
16341 ent_set_anim(self, ANI_BLOCK, 0);
16342 self->takeaction = common_block;
16343 return;
16347 if(notinair && player_check_special())
16348 return; // So you don't perform specials falling off the edge
16350 if((player[(int) self->playerindex].releasekeys & FLAG_ATTACK)) {
16351 if(self->stalltime && notinair &&
16352 ((validanim(self, ANI_CHARGEATTACK)
16353 && self->stalltime + (GAME_SPEED * self->modeldata.animation[ANI_CHARGEATTACK]->chargetime) < borTime)
16354 || (!validanim(self, ANI_CHARGEATTACK)
16355 && self->stalltime +
16356 (GAME_SPEED *
16357 self->modeldata.
16358 animation[dyn_anims.animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1]]->
16359 chargetime) < borTime))) {
16360 set_attacking(self);
16361 self->xdir = self->zdir = 0;
16362 self->combostep[0] = 0;
16364 if(validanim(self, ANI_CHARGEATTACK))
16365 ent_set_anim(self, ANI_CHARGEATTACK, 0);
16366 else
16367 ent_set_anim(self,
16368 dyn_anims.animattacks[self->modeldata.atchain[self->modeldata.chainlength - 1] - 1],
16371 sound_play_sample(samples.punch, 0, savedata.effectvol, savedata.effectvol, 100);
16373 self->stalltime = 0;
16374 self->takeaction = common_attack_proc;
16375 return;
16377 self->stalltime = 0;
16380 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK) && notinair) {
16381 player[(int) self->playerindex].playkeys -= FLAG_ATTACK;
16382 self->stalltime = 0; // Disable the attack3 stalltime
16384 if(player[(int) self->playerindex].keys & FLAG_MOVEDOWN && validanim(self, ANI_DUCKATTACK)
16385 && PLAYER_MIN_Z == PLAYER_MAX_Z) {
16386 set_attacking(self);
16387 self->xdir = self->zdir = 0;
16388 self->combostep[0] = 0;
16389 ent_set_anim(self, ANI_DUCKATTACK, 0);
16390 self->takeaction = common_attack_proc;
16391 return;
16394 if(self->running && validanim(self, ANI_RUNATTACK)) // New run attack code section
16396 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
16397 set_attacking(self);
16398 self->xdir = self->zdir = 0;
16399 self->combostep[0] = 0;
16400 self->running = 0;
16401 ent_set_anim(self, ANI_RUNATTACK, 0);
16402 self->takeaction = common_attack_proc;
16403 return;
16405 // Perform special move, Now checks custom combos
16406 if(check_combo(FLAG_ATTACK)) {
16407 //player[(int)self->playerindex].playkeys -= FLAG_SPECIAL;
16408 return;
16411 if(validanim(self, ANI_ATTACKBACKWARD) && borTime < self->movetime - (GAME_SPEED / 10) && !self->movestep && self->lastmove == FLAG_BACKWARD) // New back attacks
16413 set_attacking(self);
16414 self->xdir = self->zdir = 0;
16415 if(self->direction && (player[(int) self->playerindex].keys & FLAG_MOVERIGHT))
16416 self->direction = 0; // Since the back part of the combo will flip the player, need to flip them back around
16417 else if(!self->direction && (player[(int) self->playerindex].keys & FLAG_MOVELEFT))
16418 self->direction = 1; // Since the back part of the combo will flip the player, need to flip them back around
16420 self->combostep[0] = 0;
16421 ent_set_anim(self, ANI_ATTACKBACKWARD, 0);
16422 self->takeaction = common_attack_proc;
16423 return;
16426 if((other = find_ent_here(self, self->x, self->z, TYPE_ITEM)) && !isSubtypeTouch(other) && !other->blink
16427 && diff(self->a - (seta >= 0) * seta, other->a) < 0.1) {
16428 if(validanim(self, ANI_GET) && // so we wont get stuck
16429 //dont pickup a weapon that is not in weapon list
16430 !((isSubtypeWeapon(other) && self->modeldata.weapon
16431 && (*self->modeldata.weapon)[other->modeldata.weapnum - 1] < 0) ||
16432 //if on an real animal, can't pick up weapons
16433 (self->modeldata.animal == 2 && isSubtypeWeapon(other)))) {
16434 didfind_item(other);
16435 self->xdir = self->zdir = 0;
16436 set_getting(self);
16437 ent_set_anim(self, ANI_GET, 0);
16438 self->takeaction = common_get;
16439 execute_didhit_script(other, self, 0, 0, other->modeldata.subtype, 0, 0, 0, 0, 0); //Execute didhit script as if item "hit" collecter to allow easy item scripting.
16440 return;
16443 // Use stalltime to charge end-move
16444 self->stalltime = borTime;
16445 self->xdir = self->zdir = 0;
16447 if(!validanim(self, ANI_ATTACK1) && validanim(self, ANI_JUMP)) {
16448 // This is for Mighty
16449 self->combostep[0] = 0;
16450 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, 0, ANI_JUMP);
16451 return;
16453 if(self->weapent &&
16454 self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && validanim(self, ANI_THROWATTACK)) {
16455 set_attacking(self);
16456 ent_set_anim(self, ANI_THROWATTACK, 0);
16457 self->takeaction = common_attack_proc;
16458 } else if(perform_atchain()) {
16459 if(self->attacking)
16460 sound_play_sample(samples.punch, 0, savedata.effectvol, savedata.effectvol, 100);
16463 return;
16465 // 7-1-2005 spawn projectile end
16467 // Mighty hass no attack animations, he just jumps.
16468 if(player[(int) self->playerindex].playkeys & FLAG_JUMP && notinair) { // Added !inair(self) so players can't jump when falling into holes
16469 player[(int) self->playerindex].playkeys -= FLAG_JUMP;
16471 if(self->running) {
16472 //Slide
16473 if((player[(int) self->playerindex].keys & FLAG_MOVEDOWN) && validanim(self, ANI_RUNSLIDE)) {
16474 set_attacking(self);
16475 self->xdir = self->zdir = 0;
16476 self->combostep[0] = 0;
16477 self->running = 0;
16478 ent_set_anim(self, ANI_RUNSLIDE, 0);
16479 self->takeaction = common_attack_proc;
16480 return;
16483 if(validanim(self, ANI_RUNJUMP))
16484 tryjump(self->modeldata.runjumpheight,
16485 self->modeldata.jumpspeed * self->modeldata.runjumpdist,
16486 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_RUNJUMP);
16487 else if(validanim(self, ANI_FORWARDJUMP))
16488 tryjump(self->modeldata.runjumpheight,
16489 self->modeldata.jumpspeed * self->modeldata.runjumpdist,
16490 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_FORWARDJUMP);
16491 else if(validanim(self, ANI_JUMP))
16492 tryjump(self->modeldata.runjumpheight,
16493 self->modeldata.jumpspeed * self->modeldata.runjumpdist,
16494 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_JUMP);
16495 } else {
16496 if(check_combo(FLAG_JUMP)) { // Jump can now be used with freespecials
16497 //player[(int)self->playerindex].playkeys -= FLAG_SPECIAL;
16498 return;
16499 } else {
16500 //Slide
16501 if((player[(int) self->playerindex].keys & FLAG_MOVEDOWN) && validanim(self, ANI_SLIDE)) {
16502 set_attacking(self);
16503 self->xdir = self->zdir = 0;
16504 self->combostep[0] = 0;
16505 self->running = 0;
16506 ent_set_anim(self, ANI_SLIDE, 0);
16507 self->takeaction = common_attack_proc;
16508 return;
16511 if(!(player[(int) self->playerindex].keys & (FLAG_MOVELEFT | FLAG_MOVERIGHT))
16512 && validanim(self, ANI_JUMP)) {
16513 tryjump(self->modeldata.jumpheight, 0,
16514 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_JUMP);
16515 return;
16516 } else if((player[(int) self->playerindex].keys & FLAG_MOVELEFT))
16517 self->direction = 0;
16518 else if((player[(int) self->playerindex].keys & FLAG_MOVERIGHT))
16519 self->direction = 1;
16521 if(validanim(self, ANI_FORWARDJUMP))
16522 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed,
16523 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_FORWARDJUMP);
16524 else if(validanim(self, ANI_JUMP))
16525 tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed,
16526 (self->modeldata.jumpmovez) ? self->zdir : 0, ANI_JUMP);
16529 return;
16532 if(validanim(self, ANI_BLOCK) && self->modeldata.holdblock &&
16533 player[(int) self->playerindex].keys & FLAG_SPECIAL && notinair) {
16534 player[(int) self->playerindex].playkeys -= FLAG_SPECIAL;
16535 if(!self->blocking) {
16536 self->blocking = 1;
16537 self->xdir = self->zdir = 0;
16538 ent_set_anim(self, ANI_BLOCK, 0);
16540 return;
16541 } else {
16542 self->blocking = 0;
16545 // check attack2 - attack4 freespecial
16546 if(notinair) {
16547 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK2) && check_combo(FLAG_ATTACK2)) {
16548 player[(int) self->playerindex].playkeys -= FLAG_ATTACK2;
16549 return;
16551 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK3) && check_combo(FLAG_ATTACK3)) {
16552 player[(int) self->playerindex].playkeys -= FLAG_ATTACK3;
16553 return;
16555 if((player[(int) self->playerindex].playkeys & FLAG_ATTACK4) && check_combo(FLAG_ATTACK4)) {
16556 player[(int) self->playerindex].playkeys -= FLAG_ATTACK4;
16557 return;
16561 if(PLAYER_MIN_Z != PLAYER_MAX_Z) { // More of a platform feel
16562 if(player[(int) self->playerindex].keys & FLAG_MOVEUP) {
16563 if(!self->modeldata.runupdown)
16564 self->running = 0; // Quits running if player presses up (or the up animation exists
16566 if(validanim(self, ANI_UP) && !self->running) {
16567 action = 2;
16568 self->zdir = -self->modeldata.speed / 2; // Used for up animation
16569 } else if(self->running) {
16570 action = 4;
16571 self->zdir = -self->modeldata.runspeed / 2; // Moves up at a faster rate running
16572 } else {
16573 action = 1;
16574 self->zdir = -self->modeldata.speed / 2;
16576 } else if(player[(int) self->playerindex].keys & FLAG_MOVEDOWN) {
16577 if(!self->modeldata.runupdown)
16578 self->running = 0; // Quits running if player presses down (or the down animation exists
16580 if(validanim(self, ANI_DOWN) && !self->running) {
16581 action = 3;
16582 self->zdir = self->modeldata.speed / 2; // Used for down animation
16583 } else if(self->running) {
16584 action = 4;
16585 self->zdir = self->modeldata.runspeed / 2; // Moves down at a faster rate running
16586 } else {
16587 action = 1;
16588 self->zdir = self->modeldata.speed / 2;
16590 } else if(!(player[(int) self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN)))
16591 self->zdir = 0;
16592 } else if(validanim(self, ANI_DUCK) && player[(int) self->playerindex].keys & FLAG_MOVEDOWN && notinair) {
16593 ent_set_anim(self, ANI_DUCK, 0);
16594 self->xdir = self->zdir = 0;
16595 return;
16598 if(player[(int) self->playerindex].keys & FLAG_MOVELEFT) {
16599 if(self->direction) {
16600 self->running = 0; // Quits running if player changes direction
16601 if(self->modeldata.turndelay && !self->turntime)
16602 self->turntime = borTime + self->modeldata.turndelay;
16603 else if(self->turntime && borTime >= self->turntime) {
16604 self->turntime = 0;
16605 if(validanim(self, ANI_TURN)) {
16606 set_turning(self);
16607 ent_set_anim(self, ANI_TURN, 0);
16608 self->takeaction = common_turn;
16609 return;
16611 self->direction = 0;
16612 } else if(!self->modeldata.turndelay && validanim(self, ANI_TURN)) {
16613 set_turning(self);
16614 ent_set_anim(self, ANI_TURN, 0);
16615 self->takeaction = common_turn;
16616 return;
16617 } else if(!self->turntime)
16618 self->direction = 0;
16619 } else
16620 self->turntime = 0;
16622 if(self->running) {
16623 action = 4;
16624 self->xdir = -self->modeldata.runspeed; // If running, player moves at a faster rate
16625 } else if(action != 2 && action != 3) {
16626 action = 1;
16627 self->xdir = -self->modeldata.speed;
16628 } else {
16629 self->xdir = -self->modeldata.speed;
16631 } else if(player[(int) self->playerindex].keys & FLAG_MOVERIGHT) {
16632 if(!self->direction) {
16633 self->running = 0; // Quits running if player changes direction
16634 if(self->modeldata.turndelay && !self->turntime)
16635 self->turntime = borTime + self->modeldata.turndelay;
16636 else if(self->turntime && borTime >= self->turntime) {
16637 self->turntime = 0;
16638 if(validanim(self, ANI_TURN)) {
16639 set_turning(self);
16640 ent_set_anim(self, ANI_TURN, 0);
16641 self->takeaction = common_turn;
16642 return;
16644 self->direction = 1;
16645 } else if(!self->modeldata.turndelay && validanim(self, ANI_TURN)) {
16646 set_turning(self);
16647 ent_set_anim(self, ANI_TURN, 0);
16648 self->takeaction = common_turn;
16649 return;
16650 } else if(!self->turntime)
16651 self->direction = 1;
16652 } else
16653 self->turntime = 0;
16655 if(self->running) {
16656 action = 4;
16657 self->xdir = self->modeldata.runspeed; // If running, player moves at a faster rate
16658 } else if(action != 2 && action != 3) {
16659 action = 1;
16660 self->xdir = self->modeldata.speed;
16661 } else {
16662 self->xdir = self->modeldata.speed;
16664 } else if(!((player[(int) self->playerindex].keys & FLAG_MOVELEFT) ||
16665 (player[(int) self->playerindex].keys & FLAG_MOVERIGHT))) {
16666 self->running = 0; // Player let go of left/right and so quits running
16667 self->xdir = 0;
16668 self->turntime = 0;
16670 // ltb 1-18-05 new Item get code to address new subtype
16672 if((other = find_ent_here(self, self->x, self->z, TYPE_ITEM)) && isSubtypeTouch(other) && !other->blink &&
16673 diff(self->a - (seta >= 0) * seta, other->a) < 0.1 && action) {
16674 didfind_item(other); // Added function to clean code up a bit
16677 switch (action) {
16678 case 1:
16679 // back walk feature
16680 if(level && validanim(self, ANI_BACKWALK)) {
16681 if(self->modeldata.facing == 1 || level->facing == 1)
16682 bkwalk = !self->direction;
16683 else if(self->modeldata.facing == 2 || level->facing == 2)
16684 bkwalk = self->direction;
16685 else if((self->modeldata.facing == 3 || level->facing == 3)
16686 && (level->scrolldir & SCROLL_LEFT) && !self->direction)
16687 bkwalk = 1;
16688 else if((self->modeldata.facing == 3 || level->facing == 3)
16689 && (level->scrolldir & SCROLL_RIGHT) && self->direction)
16690 bkwalk = 1;
16691 else if(self->turntime && self->modeldata.turndelay)
16692 bkwalk = 1;
16693 if(bkwalk)
16694 common_backwalk_anim(self); //ent_set_anim(self, ANI_BACKWALK, 0);
16695 else
16696 common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0); // If neither up nor down exist, set to walk
16697 } else
16698 common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0); // If neither up nor down exist, set to walk
16699 break;
16700 case 2:
16701 common_up_anim(self); //ent_set_anim(self, ANI_UP, 0); // Set to up animation if exists
16702 break;
16703 case 3:
16704 common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0); // Set to down animation if exists
16705 break;
16706 case 4:
16707 ent_set_anim(self, ANI_RUN, 0); // Set to run animation if exists
16708 break;
16709 default:
16710 if(self->idling) {
16711 common_idle_anim(self);
16712 return;
16714 break;
16716 if(action) {
16717 self->takeaction = NULL;
16718 self->idling = 1;
16722 int common_idle_anim(entity * ent) {
16724 common_idle_anim
16725 Damon Vaughn Caskey
16726 11012009
16727 Determine and set appropriate idle animation based on condition and range.
16728 Returns 1 if any animation is set.
16731 int i; //Loop counter.
16732 int iAni; //Animation.
16734 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 (C) (TM)
16735 ent->xdir = ent->zdir = 0; //Stop movement.
16737 if(validanim(ent, ANI_FAINT) && ent->health <= ent->modeldata.health / 4) //ANI_FAINT and health at/below 25%?
16739 ent_set_anim(ent, ANI_FAINT, 0); //Set ANI_FAINT.
16740 return 1; //Return 1 and exit.
16741 } else if(validanim(ent, ANI_SLEEP) && (borTime >= ent->sleeptime) && ent->animating) //ANI_SLEEP, sleeptime up and currently animating?
16743 ent_set_anim(ent, ANI_SLEEP, 0); //Set sleep anim.
16744 return 1; //Return 1 and exit.
16745 } else {
16746 for(i = 0; i < dyn_anim_custom_maxvalues.max_idles; i++) //Loop through all idle animations.
16748 iAni = dyn_anims.animidles[i]; //Get current animation.
16750 if(validanim(ent, iAni) && iAni != ANI_IDLE) //Valid and not ANI_IDLE?
16752 if(normal_find_target(iAni)) //Opponent in range of current animation?
16754 ent_set_anim(ent, iAni, 0); //Set animation.
16755 return 1; //Return 1 and exit.
16760 if(validanim(ent, ANI_IDLE)) {
16761 ent_set_anim(ent, ANI_IDLE, 0); //No alternates were set. Set ANI_IDLE.
16762 return 1; //Return 1 and exit.
16766 return 0;
16769 int common_walk_anim(entity * ent) {
16771 common_walk_anim
16772 Damon Vaughn Caskey
16773 11032009
16774 Determine and set appropriate walk animation based on condition and range.
16775 Returns 1 if any animation is set.
16778 int i; //Loop counter.
16779 int iAni; //Animation.
16781 for(i = 0; i < dyn_anim_custom_maxvalues.max_walks; i++) //Loop through all relevant animations.
16783 iAni = dyn_anims.animwalks[i]; //Get current animation.
16785 if(validanim(ent, iAni) && iAni != ANI_WALK) //Valid and not Default animation??
16787 if(normal_find_target(iAni)) //Opponent in range of current animation?
16789 ent_set_anim(ent, iAni, 0); //Set animation.
16790 return 1; //Return 1 and exit.
16795 if(validanim(ent, ANI_WALK)) {
16796 ent_set_anim(ent, ANI_WALK, 0); //No alternates were set. Set default..
16797 return 1; //Return 1 and exit.
16800 return 0;
16803 int common_backwalk_anim(entity * ent) {
16805 common_backwalk_anim
16806 Damon Vaughn Caskey
16807 11032009
16808 Determine and set appropriate backwalk animation based on condition and range.
16809 Returns 1 if any animation is set.
16812 int i; //Loop counter.
16813 int iAni; //Animation.
16815 for(i = 0; i < dyn_anim_custom_maxvalues.max_backwalks; i++) //Loop through all relevant animations.
16817 iAni = dyn_anims.animbackwalks[i]; //Get current animation.
16819 if(validanim(ent, iAni) && iAni != ANI_BACKWALK) //Valid and not Default animation??
16821 if(normal_find_target(iAni)) //Opponent in range of current animation?
16823 ent_set_anim(ent, iAni, 0); //Set animation.
16824 return 1; //Return 1 and exit.
16829 if(validanim(ent, ANI_BACKWALK)) {
16830 ent_set_anim(ent, ANI_BACKWALK, 0); //No alternates were set. Set default..
16831 return 1; //Return 1 and exit.
16834 return 0;
16837 int common_up_anim(entity * ent) {
16839 common_up_anim
16840 Damon Vaughn Caskey
16841 11032009
16842 Determine and set appropriate up animation based on condition and range.
16843 Returns 1 if any animation is set.
16846 int i; //Loop counter.
16847 int iAni; //Animation.
16849 for(i = 0; i < dyn_anim_custom_maxvalues.max_ups; i++) //Loop through all relevant animations.
16851 iAni = dyn_anims.animups[i]; //Get current animation.
16853 if(validanim(ent, iAni) && iAni != ANI_UP) //Valid and not Default animation??
16855 if(normal_find_target(iAni)) //Opponent in range of current animation?
16857 ent_set_anim(ent, iAni, 0); //Set animation.
16858 return 1; //Return 1 and exit.
16863 if(validanim(ent, ANI_UP)) {
16864 ent_set_anim(ent, ANI_UP, 0); //No alternates were set. Set default..
16865 return 1; //Return 1 and exit.
16868 return 0;
16871 int common_down_anim(entity * ent) {
16873 common_up_anim
16874 Damon Vaughn Caskey
16875 11032009
16876 Determine and set appropriate up animation based on condition and range.
16877 Returns 1 if any animation is set.
16880 int i; //Loop counter.
16881 int iAni; //Animation.
16883 for(i = 0; i < dyn_anim_custom_maxvalues.max_downs; i++) //Loop through all relevant animations.
16885 iAni = dyn_anims.animdowns[i]; //Get current animation.
16887 if(validanim(ent, iAni) && iAni != ANI_DOWN) //Valid and not Default animation??
16889 if(normal_find_target(iAni)) //Opponent in range of current animation?
16891 ent_set_anim(ent, iAni, 0); //Set animation.
16892 return 1; //Return 1 and exit.
16897 if(validanim(ent, ANI_DOWN)) {
16898 ent_set_anim(ent, ANI_DOWN, 0); //No alternates were set. Set default..
16899 return 1; //Return 1 and exit.
16902 return 0;
16905 //ammo count goes down
16906 void subtract_shot() {
16907 if(self->weapent && self->weapent->modeldata.shootnum) {
16908 self->weapent->modeldata.shootnum--;
16909 if(!self->weapent->modeldata.shootnum) {
16910 self->weapent->modeldata.counter = 0;
16911 dropweapon(0);
16918 void dropweapon(int flag) {
16919 int wall;
16920 entity *other = NULL;
16922 if(self->weapent) {
16923 if(self->weapent->modeldata.typeshot
16924 || (!self->weapent->modeldata.typeshot && self->weapent->modeldata.shootnum)) {
16925 self->weapent->direction = self->direction; //same direction as players, 2007 -2 - 11 by UTunnels
16926 if(flag < 2)
16927 self->weapent->modeldata.counter -= flag;
16928 self->weapent->z = self->z;
16929 self->weapent->x = self->x;
16930 self->weapent->a = self->a;
16932 other = check_platform(self->weapent->x, self->weapent->z);
16933 wall = checkwall(self->weapent->x, self->weapent->z);
16935 if(other && other != self->weapent)
16936 self->weapent->base += other->a + other->animation->platform[other->animpos][7];
16937 else if(wall >= 0)
16938 self->weapent->base += level->walls[wall].alt;
16940 if(validanim(self->weapent, ANI_RESPAWN))
16941 ent_set_anim(self->weapent, ANI_RESPAWN, 0);
16942 else if(validanim(self->weapent, ANI_SPAWN))
16943 ent_set_anim(self->weapent, ANI_SPAWN, 0);
16944 else
16945 ent_set_anim(self->weapent, ANI_IDLE, 0);
16947 if(!self->weapent->modeldata.counter) {
16948 if(!self->modeldata.animal) {
16949 self->weapent->blink = 1;
16950 self->weapent->takeaction = common_lie;
16951 } else {
16952 self->weapent->modeldata.type = TYPE_NONE;
16953 self->weapent->think = runanimal;
16955 self->weapent->nextthink = borTime + 1;
16958 self->weapent = NULL;
16960 if(flag < 2) {
16961 if(self->modeldata.type == TYPE_PLAYER) {
16962 if(player[(int) self->playerindex].weapnum)
16963 set_weapon(self, player[(int) self->playerindex].weapnum, 0);
16964 else
16965 set_weapon(self, level->setweap, 0);
16966 } else
16967 set_weapon(self, 0, 0);
16970 if(self->modeldata.weaploss[1] > 0) {
16971 set_weapon(self, self->modeldata.weaploss[1], 0);
16976 int player_takedamage(entity * other, s_attack * attack) {
16977 s_attack atk;
16978 //printf("damaged by: '%s' %d\n", other->name, attack->attack_force);
16979 if(healthcheat) {
16980 memcpy(&atk, attack, sizeof(s_attack));
16981 atk.attack_force = 0;
16982 return common_takedamage(other, &atk);
16984 return common_takedamage(other, attack);
16988 ////////////////////////////////
16990 // Called when player re-enters the game.
16991 // Drop all enemies EXCEPT for the linked/frozen ones.
16992 void drop_all_enemies() {
16993 int i;
16994 entity *weapself = self;
16995 for(i = 0; i < ent_max; i++) {
16996 if(ent_list[i]->exists && ent_list[i]->health > 0 && ent_list[i]->modeldata.type == TYPE_ENEMY && !ent_list[i]->owner && // Don't want to knock down a projectile
16997 !ent_list[i]->frozen && // Don't want to unfreeze a frozen enemy
16998 !ent_list[i]->modeldata.nomove && !ent_list[i]->modeldata.nodrop && validanim(ent_list[i], ANI_FALL)) {
16999 ent_list[i]->attacking = 0;
17000 ent_list[i]->projectile = 0;
17001 ent_list[i]->takeaction = common_fall; //enemy_fall;
17002 ent_list[i]->damage_on_landing = 0;
17003 self = ent_list[i];
17004 ent_unlink(self);
17005 ent_list[i]->xdir = (self->direction) ? (-1.2) : 1.2;
17006 dropweapon(1);
17007 toss(ent_list[i], 2.5 + randf(1));
17008 set_fall(ent_list[i], ATK_NORMAL, 1, self, 0, 0, 0, 0, 0, 0);
17009 ent_list[i]->knockdowncount = ent_list[i]->modeldata.knockdowncount;
17011 ent_list[i]->knockdowntime = 0;
17014 self = weapself;
17019 // Called when boss dies
17020 void kill_all_enemies() {
17021 int i;
17022 s_attack attack;
17023 entity *tmpself = NULL;
17025 attack = emptyattack;
17026 attack.attack_type = dyn_anim_custom_maxvalues.max_attack_types;
17027 attack.dropv[0] = (float) 3;
17028 attack.dropv[1] = (float) 1.2;
17029 attack.dropv[2] = (float) 0;
17031 tmpself = self;
17032 for(i = 0; i < ent_max; i++) {
17033 if(ent_list[i]->exists
17034 && ent_list[i]->health > 0 && ent_list[i]->modeldata.type == TYPE_ENEMY && ent_list[i]->takedamage) {
17035 self = ent_list[i];
17036 attack.attack_force = self->health;
17037 self->takedamage(tmpself, &attack);
17038 self->dead = 1;
17041 self = tmpself;
17046 void smart_bomb(entity * e, s_attack * attack) // New method for smartbombs
17048 int i, hostile, hit = 0;
17049 entity *tmpself = NULL;
17051 hostile = e->modeldata.hostile;
17052 if(e->modeldata.type == TYPE_PLAYER)
17053 hostile &= ~(TYPE_PLAYER);
17055 tmpself = self;
17056 for(i = 0; i < ent_max; i++) {
17057 if(ent_list[i]->exists
17058 && ent_list[i] != e
17059 && ent_list[i]->health > 0 && (ent_list[i]->modeldata.type & (e->modeldata.hostile))) {
17060 self = ent_list[i];
17061 hit = 1; // for nocost, if the bomb doesn't hit, it won't cost energy
17062 if(self->takedamage) {
17063 //attack.attack_drop = self->modeldata.knockdowncount+1;
17064 self->takedamage(e, attack);
17065 } else {
17066 self->health -= attack->attack_force;
17067 if(self->health <= 0)
17068 kill(self);
17072 if(nocost && hit && smartbomber) // don't use e, because this can be an item-bomb
17074 self = smartbomber;
17075 if(check_energy(1, ANI_SPECIAL)) {
17076 self->mp -= self->modeldata.animation[ANI_SPECIAL]->energycost[0];
17077 } else
17078 self->health -= self->modeldata.animation[ANI_SPECIAL]->energycost[0];
17080 self = tmpself;
17085 ////////////////////////////////
17087 void anything_walk() {
17088 if(self->x < advancex - 80 || self->x > advancex + (videomodes.hRes + 80)) {
17089 kill(self);
17090 return;
17092 //self->x += self->xdir;
17095 entity *knife_spawn(char *name, int index, float x, float z, float a, int direction, int type, int map) {
17096 entity *e = NULL;
17097 float dest_a = a;
17098 int dest_index = -1;
17099 char dest_ptype = 0;
17100 char* dest_name = NULL;
17102 if(self->weapent && self->weapent->modeldata.project >= 0) {
17103 dest_index = self->weapent->modeldata.project;
17104 } else if(self->animation->custknife >= 0) {
17105 dest_index = self->animation->custknife;
17106 } else if(self->animation->custpshotno >= 0) {
17107 dest_index = self->animation->custpshotno;
17108 dest_a = 0;
17109 dest_ptype = 1;
17110 } else if(self->modeldata.knife >= 0) {
17111 dest_index = self->modeldata.knife;
17112 } else if(self->modeldata.pshotno >= 0) {
17113 dest_index = self->modeldata.pshotno;
17114 dest_a = 0;
17115 dest_ptype = 1;
17116 } else if(index >= 0 || name) {
17117 dest_index = index;
17118 dest_name = name;
17119 } else if(type) {
17120 dest_name = "Shot";
17121 } else { /* if(!type) */
17122 dest_name = "Knife";
17124 e = spawn(x, z, dest_a, direction, dest_name, dest_index, NULL);
17125 if(!e)
17126 return NULL;
17127 e->ptype = dest_ptype;
17128 e->a = dest_a;
17132 if(e == NULL)
17133 return NULL;
17134 else if(self->modeldata.type == TYPE_PLAYER)
17135 e->modeldata.type = TYPE_SHOT;
17136 else
17137 e->modeldata.type = self->modeldata.type;
17139 if(self->animation->energycost[0] > 0 && nocost)
17140 self->cantfire = 1; // Can't fire if still exists on screen
17142 if(!e->model->speed && !e->modeldata.nomove)
17143 e->modeldata.speed = 2;
17144 else if(e->modeldata.nomove)
17145 e->modeldata.speed = 0;
17147 e->owner = self; // Added so projectiles don't hit the owner
17148 e->nograb = 1; // Prevents trying to grab a projectile
17149 e->attacking = 1;
17150 //e->direction = direction;
17151 e->think = common_think;
17152 e->nextthink = borTime + 1;
17153 e->trymove = NULL;
17154 e->takedamage = arrow_takedamage;
17155 e->takeaction = NULL;
17156 e->modeldata.aimove = AIMOVE1_ARROW;
17157 if(!e->modeldata.offscreenkill)
17158 e->modeldata.offscreenkill = 200; //default value
17159 e->modeldata.aiattack = AIATTACK1_NOATTACK;
17160 e->remove_on_attack = e->modeldata.remove;
17161 e->autokill = e->modeldata.nomove;
17163 ent_set_colourmap(e, map);
17165 if(e->ptype)
17166 e->base = 0;
17167 else
17168 e->base = a;
17170 if(e->modeldata.hostile < 0)
17171 e->modeldata.hostile = self->modeldata.hostile;
17172 if(e->modeldata.candamage < 0)
17173 e->modeldata.candamage = self->modeldata.candamage;
17175 e->modeldata.subject_to_wall = e->modeldata.subject_to_platform = e->modeldata.subject_to_hole =
17176 e->modeldata.subject_to_gravity = 1;
17177 e->modeldata.no_adjust_base = 1;
17178 return e;
17183 void bomb_explode() {
17184 if(self->animating)
17185 return;
17186 kill(self);
17190 entity *bomb_spawn(char *name, int index, float x, float z, float a, int direction, int map) {
17191 entity *e = NULL;
17193 if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE
17194 && self->weapent->modeldata.project >= 0)
17195 e = spawn(x, z, a, direction, NULL, self->weapent->modeldata.project, NULL);
17196 else if(self->animation->custbomb >= 0)
17197 e = spawn(x, z, a, direction, NULL, self->animation->custbomb, NULL);
17198 else if(self->modeldata.bomb >= 0)
17199 e = spawn(x, z, a, direction, NULL, self->modeldata.bomb, NULL);
17200 else
17201 e = spawn(x, z, a, direction, name, index, NULL);
17203 if(e == NULL)
17204 return NULL;
17206 e->a = a;
17208 if(self->animation->energycost[0] > 0 && nocost)
17209 self->cantfire = 1; // Can't fire if still exists on screen
17211 if(!e->model->speed && !e->modeldata.nomove)
17212 e->modeldata.speed = 2;
17213 else if(e->modeldata.nomove)
17214 e->modeldata.speed = 0;
17216 e->attacking = 1;
17217 e->owner = self; // Added so projectiles don't hit the owner
17218 e->nograb = 1; // Prevents trying to grab a projectile
17219 e->toexplode = 1; // Set to distinguish exploding projectiles and also so stops falling when hitting an opponent
17220 ent_set_colourmap(e, map);
17221 //e->direction = direction;
17222 toss(e, e->modeldata.jumpheight);
17223 e->think = common_think;
17224 e->nextthink = borTime + 1;
17225 e->trymove = NULL;
17226 e->takeaction = NULL;
17227 e->modeldata.aimove = AIMOVE1_BOMB;
17228 e->modeldata.aiattack = AIATTACK1_NOATTACK; // Well, bomb's attack animation is passive, dont use any A.I. code.
17229 e->takedamage = common_takedamage;
17230 e->remove_on_attack = 0;
17231 e->autokill = e->modeldata.nomove;
17234 // Ok, some old mods use type none, will have troubles.
17235 // so we give them some default hostile types.
17236 if(e->modeldata.hostile < 0)
17237 e->modeldata.hostile = self->modeldata.hostile;
17238 if(e->modeldata.candamage < 0)
17239 e->modeldata.candamage = self->modeldata.candamage;
17240 e->modeldata.no_adjust_base = 0;
17241 e->modeldata.subject_to_wall = e->modeldata.subject_to_platform = e->modeldata.subject_to_hole =
17242 e->modeldata.subject_to_gravity = 1;
17243 return e;
17247 // Spawn 3 stars
17248 int star_spawn(float x, float z, float a, int direction) { // added entity to know which star to load
17249 entity *e = NULL;
17250 int i, index = -1;
17251 char *starname = NULL;
17252 float fd = (float) ((direction ? 2 : -2));
17254 //merge enemy/player together, use the same rules
17255 if(self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE
17256 && self->weapent->modeldata.project >= 0)
17257 index = self->weapent->modeldata.project;
17258 else if(self->animation->custstar >= 0)
17259 index = self->animation->custstar; //use any star
17260 else if(self->modeldata.star >= 0)
17261 index = self->modeldata.star;
17262 else
17263 starname = "Star"; // this is default star
17265 for(i = 0; i < 3; i++) {
17266 e = spawn(x, z, a, direction, starname, index, NULL);
17267 if(e == NULL)
17268 return 0;
17270 self->attacking = 0;
17272 e->takedamage = arrow_takedamage; //enemy_takedamage; // Players can now hit projectiles
17273 e->owner = self; // Added so enemy projectiles don't hit the owner
17274 e->attacking = 1;
17275 e->nograb = 1; // Prevents trying to grab a projectile
17276 e->xdir = fd / 2 * (float) i;
17277 e->think = common_think;
17278 e->nextthink = borTime + 1;
17279 e->trymove = NULL;
17280 e->takeaction = NULL;
17281 e->modeldata.aimove = AIMOVE1_STAR;
17282 e->modeldata.aiattack = AIATTACK1_NOATTACK;
17283 e->remove_on_attack = e->modeldata.remove;
17284 e->base = a;
17285 e->a = a;
17286 //e->direction = direction;
17288 if(e->modeldata.hostile < 0)
17289 e->modeldata.hostile = self->modeldata.hostile;
17290 if(e->modeldata.candamage < 0)
17291 e->modeldata.candamage = self->modeldata.candamage;
17293 e->modeldata.subject_to_wall = e->modeldata.subject_to_platform =
17294 e->modeldata.subject_to_hole = e->modeldata.subject_to_gravity = 1;
17295 e->modeldata.no_adjust_base = 1;
17297 return 1;
17302 void steam_think() {
17303 if(!self->animating) {
17304 kill(self);
17305 return;
17308 self->base += 1;
17309 self->a = self->base;
17314 // for the "trap" type 7-1-2005 trap start
17315 void trap_think() {
17316 if(self->x < advancex - 80 || self->x > advancex + (videomodes.hRes + 80)) {
17317 // kill(self); // 6-2-2005 removed temporarily
17318 return;
17321 self->attacking = 1;
17322 self->nextthink = borTime + 1;
17325 // 7-1-2005 trap end
17330 void steam_spawn(float x, float z, float a) {
17331 entity *e = NULL;
17333 e = spawn(x, z, a, 0, "Steam", -1, NULL);
17335 if(e == NULL)
17336 return;
17338 e->base = a;
17339 e->modeldata.no_adjust_base = 1;
17340 e->think = steam_think;
17345 void steamer_think() {
17346 if(self->x < advancex - 80 || self->x > advancex + (videomodes.hRes + 80)) {
17347 kill(self);
17348 return;
17351 steam_spawn(self->x, self->z, self->a);
17352 self->nextthink = borTime + (GAME_SPEED / 10) + (rand32() & 31);
17357 void text_think() { // New function so text can be displayed
17358 // wait to suicide
17359 if(!self->animating)
17360 kill(self);
17363 ////////////////////////////////
17365 //homing arrow find its target
17366 // type : target type
17367 entity *homing_find_target(int type) {
17368 int i, min, max;
17369 int index = -1;
17370 //use the walk animation's range
17371 if(validanim(self, ANI_WALK)) {
17372 min = self->modeldata.animation[ANI_WALK]->range[0];
17373 max = self->modeldata.animation[ANI_WALK]->range[1];
17374 } else {
17375 min = 0;
17376 max = 999;
17378 //find the 'nearest' one
17379 for(i = 0; i < ent_max; i++) {
17380 if(ent_list[i]->exists && ent_list[i] != self //cant target self
17381 && (ent_list[i]->modeldata.type & type)
17382 && diff(ent_list[i]->x, self->x) + diff(ent_list[i]->z, self->z) >= min
17383 && diff(ent_list[i]->x, self->x) + diff(ent_list[i]->z, self->z) <= max
17384 && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]) {
17385 if(index < 0
17386 || diff(ent_list[i]->x, self->x) + diff(ent_list[i]->z, self->z) < diff(ent_list[index]->x,
17387 self->x) +
17388 diff(ent_list[index]->z, self->z))
17389 index = i;
17392 if(index >= 0)
17393 return ent_list[index];
17394 return NULL;
17398 void bike_crash() {
17399 int i;
17400 if(self->direction)
17401 self->xdir = 2;
17402 else
17403 self->xdir = -2;
17404 self->nextthink = borTime + THINK_SPEED / 2;
17405 for(i = 0; i < maxplayers[current_set]; i++)
17406 control_rumble(i, 100);
17407 //if(self->x < advancex-100 || self->x > advancex+(videomodes.hRes+100)) kill(self);
17412 int biker_takedamage(entity * other, s_attack * attack) {
17413 entity *driver = NULL;
17414 entity *tempself = NULL;
17415 if(self->dead)
17416 return 0;
17417 // Fell in a hole
17418 if(self->a < PIT_DEPTH) {
17419 kill(self);
17420 return 0;
17422 if(other != self)
17423 set_opponent(other, self);
17425 if(attack->no_pain) // don't drop driver until it is dead, because the attack has no pain effect
17427 checkdamage(other, attack);
17428 if(self->health > 0)
17429 return 1; // not dead yet
17432 set_pain(self, self->damagetype, 1);
17433 self->attacking = 1;
17434 if(!self->modeldata.offscreenkill)
17435 self->modeldata.offscreenkill = 100;
17436 self->think = bike_crash;
17437 self->nextthink = borTime + THINK_SPEED;
17438 // well, this is the real entity, the driver who take the damage
17439 if((driver = drop_driver(self))) {
17440 driver->a = self->a;
17441 tempself = self;
17442 self = driver;
17443 self->drop = 1;
17444 self->direction = tempself->direction;
17445 if(self->takedamage)
17446 self->takedamage(self, attack);
17447 else
17448 self->health -= attack->attack_force;
17449 self = tempself;
17452 self->health = 0;
17453 checkdeath();
17454 return 1;
17459 void obstacle_fall() {
17460 if(inair(self))
17461 return;
17463 self->xdir = self->zdir = 0;
17464 if((!self->animating && validanim(self, ANI_DIE)) || !validanim(self, ANI_DIE))
17465 kill(self); // Fixed so ANI_DIE can be used
17470 void obstacle_fly() // Now obstacles can fly when hit like on Simpsons/TMNT
17472 //self->x += self->xdir * 4; // Equivelant of speed 40
17473 if(self->x > advancex + (videomodes.hRes + 200) || self->x < advancex - 200)
17474 kill(self);
17476 self->nextthink = borTime + 2;
17481 int obstacle_takedamage(entity * other, s_attack * attack) {
17482 if(self->a <= PIT_DEPTH) {
17483 kill(self);
17484 return 0;
17487 self->pain_time = borTime + (GAME_SPEED / 5);
17488 set_opponent(other, self);
17489 if(self->opponent && self->opponent->modeldata.type == TYPE_PLAYER) {
17490 control_rumble(self->opponent->playerindex, 75);
17492 checkdamage(other, attack);
17493 self->playerindex = other->playerindex; // Added so points go to the correct player
17494 addscore(other->playerindex, attack->attack_force * self->modeldata.multiple); // Points can now be given for hitting an obstacle
17496 if(self->health <= 0) {
17498 checkdeath();
17500 if(other->x < self->x)
17501 self->xdir = 1;
17502 else
17503 self->xdir = -1;
17505 self->attacking = 1; // So obstacles can explode and hurt players/enemies
17507 if(self->modeldata.subtype == SUBTYPE_FLYDIE) { // Now obstacles can fly like on Simpsons/TMNT
17508 self->xdir *= 4;
17509 self->think = obstacle_fly;
17510 ent_set_anim(self, ANI_FALL, 0);
17511 } else {
17512 self->think = obstacle_fall;
17514 if(validanim(self, ANI_DIE))
17515 ent_set_anim(self, ANI_DIE, 0); // LTB 1-13-05 Die before toss
17516 else {
17517 toss(self, self->modeldata.jumpheight / 1.333);
17518 ent_set_anim(self, ANI_FALL, 0);
17521 if(!self->modeldata.nodieblink)
17522 self->blink = 1;
17526 self->nextthink = borTime + 1;
17527 return 1;
17529 static void setDestIfSource_char(char* dest, char source) {
17530 if(source)
17531 *dest = source;
17533 static void setDestIfSource_int(int* dest, int source) {
17534 if(source)
17535 *dest = source;
17538 entity *smartspawn(s_spawn_entry * props) { // 7-1-2005 Entire section replaced with lord balls code
17539 entity *e = NULL;
17540 entity *wp = NULL;
17541 int playercount;
17543 if(props == NULL || level == NULL)
17544 return NULL;
17546 // Now you can make it so enemies/obstacles/etc only spawn if there are 2 players
17547 if(props->spawnplayer_count >= (playercount = count_ents(TYPE_PLAYER))) {
17548 if(props->boss)
17549 --level->bosses;
17550 return NULL;
17553 if((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD))
17554 e = spawn(props->x, props->z + advancey, props->a, props->flip, props->name, props->index,
17555 props->model);
17556 else
17557 e = spawn(props->x + advancex, props->z, props->a, props->flip, props->name, props->index,
17558 props->model);
17561 if(e == NULL)
17562 return NULL;
17564 //printf("%s, (%f, %f, %f) - (%f, %f, %f)", props->name, props->x, props->z, props->a, e->x, e->z, e->a);
17566 // Alias?
17567 if(props->alias[0])
17568 strncpy(e->name, props->alias, MAX_NAME_LEN);
17569 if(props->item)
17570 e->item = props->itemindex;
17571 if(props->itemalias[0])
17572 strncpy(e->itemalias, props->itemalias, MAX_NAME_LEN);
17573 e->itemplayer_count = props->itemplayer_count;
17575 setDestIfSource_int(&e->itemhealth, props->itemhealth);
17576 setDestIfSource_int(&e->health, props->health[playercount - 1]);
17577 setDestIfSource_int(&e->mp, props->mp);
17578 setDestIfSource_int((int*) &e->modeldata.score, props->score);
17579 setDestIfSource_int(&e->modeldata.multiple, props->multiple);
17580 setDestIfSource_char(&e->itemtrans, props->itemtrans);
17581 setDestIfSource_char(&e->modeldata.alpha, props->alpha);
17582 setDestIfSource_char(&e->spawntype, props->spawntype);
17583 setDestIfSource_char(&e->itemmap, props->itemmap);
17585 if(!e->map && props->colourmap) {
17586 ent_set_colourmap(e, props->colourmap);
17589 if(props->aggression)
17590 e->modeldata.aggression = props->aggression; // Aggression can be changed with spawn points now
17593 // Feb 26, 2005 - Store the original map to be able to restore with dying flash
17594 if(props->dying) {
17595 e->dying = props->dying; // Feb 26, 2005 - Used to define which colourmap is used for the dying flash
17596 e->per1 = props->per1; // Mar 21, 2005 - Used to store custom percentages
17597 e->per2 = props->per2; // Mar 21, 2005 - Used to store custom percentages
17600 if(props->nolife)
17601 e->modeldata.nolife = props->nolife; // Overwrite whether live is visible or not
17602 e->boss = props->boss;
17604 if(props->boss && level && level->bossmusic[0]) {
17605 music(level->bossmusic, 1, level->bossmusic_offset);
17607 // give the entity a weapon item
17608 if(props->weapon) {
17609 wp = spawn(e->x, 100000, 0, 0, props->weapon, props->weaponindex, props->weaponmodel);
17610 //ent_default_init(wp);
17611 set_weapon(e, wp->modeldata.weapnum, 0);
17612 e->weapent = wp;
17614 //ent_default_init(e);
17615 execute_onspawn_script(e);
17616 execute_spawn_script(props, e);
17617 return e;
17618 } // 7-1-2005 replaced section ends here
17622 void spawnplayer(int index) {
17623 s_spawn_entry p;
17624 //s_model * model = NULL;
17625 int wall;
17626 int xc, zc, find = 0;
17627 index &= 3;
17629 // model = find_model(player[index].name);
17630 // if(model == NULL) return;
17632 memset(&p, 0, sizeof(s_spawn_entry));
17633 p.name = player[index].name;
17634 p.index = -1;
17635 p.itemindex = -1;
17636 p.weaponindex = -1;
17637 p.colourmap = player[index].colourmap;
17638 p.spawnplayer_count = -1;
17640 if(level->scrolldir & SCROLL_LEFT) {
17641 if(level->spawn[index][0])
17642 p.x = (float) (videomodes.hRes - level->spawn[index][0]);
17643 else
17644 p.x = (float) ((videomodes.hRes - 20) - 30 * index);
17645 } else {
17646 if(level->spawn[index][0])
17647 p.x = (float) (level->spawn[index][0]);
17648 else
17649 p.x = (float) (20 + 30 * index);
17650 p.flip = 1;
17652 if(level->spawn[index][1]) {
17653 if(level->scrolldir & (SCROLL_INWARD | SCROLL_OUTWARD))
17654 p.z = (float) (level->spawn[index][1]);
17655 else
17656 p.z = (float) (PLAYER_MIN_Z + level->spawn[index][1]);
17657 } else if(PLAYER_MAX_Z - PLAYER_MIN_Z > 5)
17658 p.z = (float) (PLAYER_MIN_Z + 5);
17659 else
17660 p.z = (float) PLAYER_MIN_Z;
17661 //////////////////checking holes/ walls///////////////////////////////////
17662 for(xc = 0; xc < videomodes.hRes / 4; xc++) {
17663 if(p.x > videomodes.hRes)
17664 p.x -= videomodes.hRes;
17665 if(p.x < 0)
17666 p.x += videomodes.hRes;
17667 if(PLAYER_MIN_Z == PLAYER_MAX_Z) {
17668 wall = checkwall(advancex + p.x, p.z);
17669 if(wall >= 0 && level->walls[wall].alt < MAX_WALL_HEIGHT)
17670 break; //found
17671 if(checkhole(advancex + p.x, p.z) || (wall >= 0 && level->walls[wall].alt >= MAX_WALL_HEIGHT))
17672 find = 0;
17673 else
17674 break; // found
17675 } else
17676 for(zc = 0; zc < (PLAYER_MAX_Z - PLAYER_MIN_Z) / 3; zc++, p.z += 3) {
17677 if(p.z > PLAYER_MAX_Z)
17678 p.z -= PLAYER_MAX_Z - PLAYER_MIN_Z;
17679 if(p.z < PLAYER_MIN_Z)
17680 p.z += PLAYER_MAX_Z - PLAYER_MIN_Z;
17681 wall = checkwall(advancex + p.x, p.z);
17682 if(wall >= 0 && level->walls[wall].alt < MAX_WALL_HEIGHT) {
17683 find = 1;
17684 break;
17685 } else if(wall >= 0 && level->walls[wall].alt >= MAX_WALL_HEIGHT)
17686 continue;
17687 if(checkhole(advancex + p.x, p.z))
17688 continue;
17689 find = 1;
17690 break;
17692 if(find)
17693 break;
17694 p.x += (level->scrolldir & SCROLL_LEFT) ? -4 : 4;
17696 ///////////////////////////////////////////////////////////////////////
17697 currentspawnplayer = index;
17698 player[index].ent = smartspawn(&p);
17700 if(player[index].ent == NULL)
17701 shutdown(1, "Fatal: unable to spawn player from '%s'", &p.name);
17703 player[index].ent->playerindex = index;
17704 if(nomaxrushreset[4] >= 1)
17705 player[index].ent->rush[1] = nomaxrushreset[index];
17706 else
17707 player[index].ent->rush[1] = 0;
17709 if(player[index].spawnhealth)
17710 player[index].ent->health = player[index].spawnhealth + 5;
17711 if(player[index].ent->health > player[index].ent->modeldata.health)
17712 player[index].ent->health = player[index].ent->modeldata.health;
17714 //mp little recorver after a level by tails
17715 if(player[index].spawnmp)
17716 player[index].ent->mp = player[index].spawnmp + 2;
17717 if(player[index].ent->mp > player[index].ent->modeldata.mp)
17718 player[index].ent->mp = player[index].ent->modeldata.mp;
17720 if(player[index].weapnum)
17721 set_weapon(player[index].ent, player[index].weapnum, 0);
17722 else
17723 set_weapon(player[index].ent, level->setweap, 0);
17730 void time_over() {
17731 int i;
17732 s_attack attack;
17734 attack = emptyattack;
17735 attack.attack_type = dyn_anim_custom_maxvalues.max_attack_types;
17736 attack.dropv[0] = (float) 3;
17737 attack.dropv[1] = (float) 1.2;
17738 attack.dropv[2] = (float) 0;
17739 if(level->type == 1)
17740 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
17741 else if(!level_completed) {
17742 endgame = 1;
17743 for(i = 0; i < 4; i++) {
17744 if(player[i].ent) {
17745 endgame = 0;
17746 self = player[i].ent;
17747 attack.attack_force = self->health;
17748 self->takedamage(self, &attack);
17752 sound_play_sample(samples.timeover, 0, savedata.effectvol, savedata.effectvol, 100);
17754 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
17755 if(!endgame)
17756 showtimeover = 1;
17761 // ----------------------- Update functions ------------------------------
17763 void update_scroller() {
17764 int to = 0, i;
17765 int numplay = 0; //4player
17766 float tx = advancex, ty = advancey;
17767 static int scrolladd = 0;
17769 scrolldx = scrolldy = 0;
17771 if(borTime < level->advancetime || freezeall)
17772 return; // Added freezeall so backgrounds/scrolling don't update if animations are frozen
17774 //level->advancetime = time + (GAME_SPEED/100); // Changed so scrolling speeds up for faster players
17775 level->advancetime = borTime - ((player[0].ent && (player[0].ent->modeldata.speed >= 12 || player[0].ent->modeldata.runspeed >= 12)) || (player[1].ent && (player[1].ent->modeldata.speed >= 12 || player[1].ent->modeldata.runspeed >= 12)) || (player[2].ent && (player[2].ent->modeldata.speed >= 12 || player[2].ent->modeldata.runspeed >= 12)) || (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
17777 if(level_completed)
17778 return;
17780 if(current_spawn >= level->numspawns && !findent(TYPE_ENEMY) &&
17781 ((player[0].ent && !player[0].ent->dead) || (player[1].ent && !player[1].ent->dead)
17782 || (player[2].ent && !player[2].ent->dead) || (player[3].ent && !player[3].ent->dead))
17784 if(!findent(TYPE_ENDLEVEL) && ((!findent(TYPE_ITEM) && !findent(TYPE_OBSTACLE) && level->type) || level->type != 1)) { // Feb 25, 2005 - Added so obstacles
17785 level_completed = 1; // can be used for bonus levels
17787 } else if(count_ents(TYPE_ENEMY) < groupmin) {
17788 while(count_ents(TYPE_ENEMY) < groupmax &&
17789 current_spawn < level->numspawns && level->pos >= level->spawnpoints[current_spawn].at) {
17790 if(level->spawnpoints[current_spawn].musicfade) {
17791 musicfade[0] = (float) level->spawnpoints[current_spawn].musicfade;
17792 musicfade[1] = (float) savedata.musicvol;
17794 if(level->spawnpoints[current_spawn].music[0]) {
17795 strncpy(musicname, level->spawnpoints[current_spawn].music, 128);
17796 musicoffset = level->spawnpoints[current_spawn].musicoffset;
17797 musicloop = 1;
17799 if(level->spawnpoints[current_spawn].wait) {
17800 level->waiting = 1;
17801 go_time = 0;
17802 } else if(level->spawnpoints[current_spawn].groupmin
17803 || level->spawnpoints[current_spawn].groupmax) {
17804 groupmin = level->spawnpoints[current_spawn].groupmin;
17805 groupmax = level->spawnpoints[current_spawn].groupmax;
17806 } else if(level->spawnpoints[current_spawn].nojoin != 0) {
17807 nojoin = (level->spawnpoints[current_spawn].nojoin == 1);
17808 } else if(level->spawnpoints[current_spawn].scrollminz ||
17809 level->spawnpoints[current_spawn].scrollmaxz) {
17810 scrollminz = (float) level->spawnpoints[current_spawn].scrollminz;
17811 scrollmaxz = (float) level->spawnpoints[current_spawn].scrollmaxz;
17812 if(!borTime)
17813 advancey = scrollminz; // reset y if spawn at very beginning
17814 } else if(level->spawnpoints[current_spawn].blockade) {
17815 // assume level spawn entry will not roll back, so just change it to 0 here
17816 if(level->spawnpoints[current_spawn].blockade < 0)
17817 level->spawnpoints[current_spawn].blockade = 0;
17818 blockade = (float) level->spawnpoints[current_spawn].blockade;
17819 } else if(level->spawnpoints[current_spawn].palette != 0) {
17820 // assume level spawn entry will not roll back, so just change it to 0 here
17821 if(level->spawnpoints[current_spawn].palette < 0)
17822 level->spawnpoints[current_spawn].palette = 0;
17823 change_system_palette(level->spawnpoints[current_spawn].palette);
17824 } else if(level->spawnpoints[current_spawn].light[1]) { // change light direction for gfxshadow
17825 light[0] = level->spawnpoints[current_spawn].light[0];
17826 light[1] = level->spawnpoints[current_spawn].light[1];
17827 } else if(level->spawnpoints[current_spawn].shadowcolor) { // change color for gfxshadow
17828 colors.shadow = level->spawnpoints[current_spawn].shadowcolor;
17829 if(colors.shadow == -1)
17830 colors.shadow = 0;
17831 else if(colors.shadow == -2)
17832 colors.shadow = -1;
17833 } else if(level->spawnpoints[current_spawn].shadowalpha) { // change color for gfxshadow
17834 shadowalpha = level->spawnpoints[current_spawn].shadowalpha;
17835 if(shadowalpha == -1)
17836 shadowalpha = 0;
17837 } else
17838 smartspawn(&level->spawnpoints[current_spawn]);
17839 ++current_spawn;
17843 for(i = 0; i < maxplayers[current_set]; i++) {
17844 if(player[i].ent)
17845 numplay++;
17848 if(level->waiting) {
17849 // Wait for all enemies to be defeated
17850 if(!findent(TYPE_ENEMY)) {
17851 level->waiting = 0;
17852 if(level->noreset <= 1)
17853 timeleft = level->settime * COUNTER_SPEED; // Feb 24, 2005 - This line moved here to set custom time
17854 go_time = borTime + 3 * GAME_SPEED;
17857 if(numplay == 0)
17858 return;
17859 if(!level->waiting) {
17860 if(level->scrolldir & SCROLL_RIGHT) {
17862 for(i = 0; i < maxplayers[current_set]; i++) {
17863 if(player[i].ent) {
17864 to += (int) player[i].ent->x;
17868 to /= numplay;
17869 to -= (videomodes.hRes / 2);
17871 to += level->cameraxoffset;
17873 if((level->scrolldir & SCROLL_BACK) && to < blockade)
17874 to = (int) blockade;
17876 if(to > advancex) {
17877 if(to > advancex + 1)
17878 to = (int) (advancex + 1);
17879 advancex = (float) to;
17882 if(level->scrolldir & SCROLL_BACK) { // Can't go back to the beginning
17884 if(to < advancex && to > blockade) {
17885 if(to < advancex - 1)
17886 to = (int) (advancex - 1);
17887 advancex = (float) to;
17891 if(advancex > level->width - videomodes.hRes)
17892 advancex = (float) level->width - videomodes.hRes;
17893 if(advancex < 0)
17894 advancex = 0;
17896 if(level->width - level->pos > videomodes.hRes)
17897 level->pos = (int) advancex;
17898 else
17899 level->pos++;
17900 } else if(level->scrolldir & SCROLL_LEFT) {
17902 for(i = 0; i < maxplayers[current_set]; i++) {
17903 if(player[i].ent) {
17904 to += (int) player[i].ent->x;
17908 to /= numplay;
17909 to -= (videomodes.hRes / 2);
17911 if(to < advancex) {
17912 if(to < advancex - 1)
17913 to = (int) (advancex - 1);
17914 advancex = (float) to;
17916 if(level->scrolldir & SCROLL_BACK) { // Can't go back to the beginning
17918 if(to > advancex) {
17919 if(to > advancex + 1)
17920 to = (int) (advancex + 1);
17921 advancex = (float) to;
17924 if(advancex > level->width - videomodes.hRes)
17925 advancex = (float) level->width - videomodes.hRes;
17926 if((level->scrolldir & SCROLL_BACK) && level->width - videomodes.hRes - advancex < blockade)
17927 advancex = level->width - videomodes.hRes - blockade;
17928 if(advancex < 0)
17929 advancex = 0;
17931 if(level->width - level->pos > videomodes.hRes)
17932 level->pos = (int) ((level->width - videomodes.hRes) - advancex);
17933 else
17934 level->pos++;
17935 } else if(level->scrolldir & SCROLL_OUTWARD) { // z scroll only
17937 for(i = 0; i < maxplayers[current_set]; i++) {
17938 if(player[i].ent) {
17939 to += (int) player[i].ent->z;
17943 to /= numplay;
17944 to -= (videomodes.vRes / 2);
17946 if(to > advancey) {
17947 if(to > advancey + 1)
17948 to = (int) (advancey + 1);
17949 advancey = (float) to;
17952 if(level->scrolldir & SCROLL_BACK) { // Can't go back to the beginning
17954 if(to < advancey) {
17955 if(to < advancey - 1)
17956 to = (int) (advancey - 1);
17957 advancey = (float) to;
17961 if(advancey > panel_height - videomodes.vRes)
17962 advancey = (float) panel_height - videomodes.vRes;
17963 if((level->scrolldir & SCROLL_BACK) && advancey < blockade)
17964 advancey = blockade;
17965 if(advancey < 4)
17966 advancey = 4;
17968 if(panel_height - level->pos > videomodes.vRes)
17969 level->pos = (int) advancey;
17970 else
17971 level->pos++;
17972 } else if(level->scrolldir & SCROLL_INWARD) {
17973 for(i = 0; i < maxplayers[current_set]; i++) {
17974 if(player[i].ent) {
17975 to += (int) player[i].ent->z;
17979 to /= numplay;
17980 to -= (videomodes.vRes / 2);
17982 if(to < advancey) {
17983 if(to < advancey - 1)
17984 to = (int) advancey - 1;
17985 advancey = (float) to;
17987 if(level->scrolldir & SCROLL_BACK) { // Can't go back to the beginning
17989 if(to > advancey) {
17990 if(to > advancey + 1)
17991 to = (int) advancey + 1;
17992 advancey = (float) to;
17995 if(advancey > panel_height - videomodes.vRes)
17996 advancey = (float) panel_height - videomodes.vRes;
17997 if((level->scrolldir & SCROLL_BACK) && panel_height - videomodes.vRes - advancey < blockade)
17998 advancey = panel_height - videomodes.vRes - blockade;
17999 if(advancey < 4)
18000 advancey = 4;
18002 if(panel_height - level->pos > videomodes.vRes)
18003 level->pos = (int) ((panel_height - videomodes.vRes) - advancey);
18004 else
18005 level->pos++;
18007 //up down, elevator stage
18008 else if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN)) {
18009 //advancey += 0.5;
18010 if(scrolladd == 1) {
18011 scrolladd = 0;
18012 advancey++;
18013 } else {
18014 scrolladd++;
18016 level->pos = (int) advancey;
18018 } //if(!level->waiting)
18020 // z auto-scroll, 2007 - 2 - 10 by UTunnels
18021 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
18023 for(i = 0, to = 0; i < maxplayers[current_set]; i++) {
18024 if(player[i].ent) {
18025 to += (int) (player[i].ent->z - (cameratype == 1 ? player[i].ent->a : 0));
18029 to /= numplay;
18030 to -= (videomodes.vRes / 2);
18032 to += level->camerazoffset;
18034 // new scroll limit
18035 if(scrollmaxz && to > scrollmaxz)
18036 to = (int) scrollmaxz;
18037 if(scrollminz && to < scrollminz)
18038 to = (int) scrollminz;
18040 if(to > advancey) {
18041 if(to > advancey + 1)
18042 to = (int) advancey + 1;
18043 advancey = (float) to;
18046 if(to < advancey) {
18047 if(to < advancey - 1)
18048 to = (int) advancey - 1;
18049 advancey = (float) to;
18052 if(advancey > panel_height - 16 - videomodes.vRes)
18053 advancey = (float) (panel_height - 16 - videomodes.vRes);
18054 if(advancey < 4)
18055 advancey = 4;
18057 // now x auto scroll
18058 else if((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD)) {
18059 for(i = 0, to = 0; i < maxplayers[current_set]; i++) {
18060 if(player[i].ent) {
18061 to += (int) player[i].ent->x;
18065 to /= numplay;
18066 to -= (videomodes.hRes / 2);
18068 // new scroll limit
18069 if(scrollmaxz && to > scrollmaxz)
18070 to = (int) scrollmaxz;
18071 if(scrollminz && to < scrollminz)
18072 to = (int) scrollminz;
18074 if(to > advancex) {
18075 if(to > advancex + 1)
18076 to = (int) advancex + 1;
18077 advancex = (float) to;
18080 if(to < advancex) {
18081 if(to < advancex - 1)
18082 to = (int) advancex - 1;
18083 advancex = (float) to;
18086 if(advancex > level->width - videomodes.hRes)
18087 advancex = (float) (level->width - videomodes.hRes);
18088 if(advancex < 0)
18089 advancex = 0;
18091 //end of z auto-scroll
18092 // global value for type_panel
18093 scrolldx = advancex - tx;
18094 scrolldy = advancey - ty;
18098 void applybglayers(s_screen * pbgscreen) {
18099 int index, x, z, i, j, k, l, timevar;
18100 s_bglayer *bglayer;
18101 int width, height;
18102 s_drawmethod screenmethod;
18104 if(!textbox)
18105 bgtravelled += (((borTime - traveltime) * level->bgspeed / 30 * 4) + ((level->rocking) ? ((borTime - traveltime) / (GAME_SPEED / 30)) : 0)); // no like in real life, maybe
18106 else
18107 texttime += borTime - traveltime;
18109 timevar = borTime - texttime;
18111 for(index = 0; index < level->numbglayers; index++) {
18112 bglayer = level->bglayers + index;
18115 if(!bglayer->xrepeat || !bglayer->zrepeat || !bglayer->enabled)
18116 continue;
18118 width = bglayer->width + bglayer->xspacing;
18119 height = bglayer->height + bglayer->zspacing;
18121 //if(level->bgdir==0) // count from left
18123 x = (int) (bglayer->xoffset + (advancex) * (bglayer->xratio) - advancex -
18124 (int) (bgtravelled * (1 - bglayer->xratio) * bglayer->bgspeedratio) % width);
18126 //else //count from right, complex
18128 // x = videomodes.hRes1 - (level->width-videomodes.hRes1-advancex)*(bglayer->xratio) - bglayer->xoffset - width*bglayer->xrepeat + bglayer->xspacing + (int)(bgtravelled * (1-bglayer->xratio) * bglayer->bgspeedratio)%width;
18131 if(level->scrolldir & SCROLL_UP) {
18132 z = (int) (4 + videomodes.vRes + (advancey + 4) * bglayer->zratio - bglayer->zoffset -
18133 height * bglayer->zrepeat + height + bglayer->zspacing);
18134 } else {
18135 z = (int) (4 + bglayer->zoffset + (advancey - 4) * bglayer->zratio - advancey);
18138 if(x < 0) {
18139 i = (-x) / width;
18140 x %= width;
18141 } else
18142 i = 0;
18143 if(z < 0) {
18144 j = (-z) / height;
18145 z %= height;
18146 } else
18147 j = 0;
18149 screenmethod = plainmethod;
18150 screenmethod.table =
18151 (pixelformat == PIXEL_x8) ? (current_palette >
18152 0 ? (level->palettes[current_palette - 1]) : NULL) : NULL;
18153 screenmethod.alpha = bglayer->alpha;
18154 screenmethod.transbg = bglayer->transparency;
18155 for(; j < bglayer->zrepeat && z < videomodes.vRes; z += height, j++) {
18156 for(k = i, l = x; k < bglayer->xrepeat && l < videomodes.hRes + bglayer->amplitude * 2;
18157 l += width, k++) {
18158 if(bglayer->type == bg_screen) {
18159 if(bglayer->watermode && bglayer->amplitude)
18160 putscreen_water(pbgscreen, bglayer->screen, l, z, bglayer->amplitude,
18161 (float) bglayer->wavelength,
18162 (int) (timevar * bglayer->wavespeed),
18163 bglayer->watermode, &screenmethod);
18164 else
18165 putscreen(pbgscreen, bglayer->screen, l, z, &screenmethod);
18166 } else if(bglayer->type == bg_sprite)
18167 putsprite(l, z, bglayer->sprite, pbgscreen, &screenmethod);
18173 traveltime = borTime;
18176 void applyfglayers(s_screen * pbgscreen) {
18177 int index, x, z, i, j, k, l;
18178 s_fglayer *fglayer;
18179 int width, height;
18180 s_drawmethod screenmethod;
18182 for(index = 0; index < level->numfglayers; index++) {
18183 fglayer = level->fglayers + index;
18186 if(!fglayer->xrepeat || !fglayer->zrepeat || !fglayer->enabled)
18187 continue;
18189 width = fglayer->width + fglayer->xspacing;
18190 height = fglayer->height + fglayer->zspacing;
18192 //if(level->bgdir==0) // count from left
18194 x = (int) (fglayer->xoffset + (advancex) * (fglayer->xratio) - advancex -
18195 (int) (bgtravelled * (1 - fglayer->xratio) * fglayer->bgspeedratio) % width);
18197 //else //count from right, complex
18199 // x = videomodes.hRes1 - (level->width-videomodes.hRes1-advancex)*(fglayer->xratio) - fglayer->xoffset - width*fglayer->xrepeat + fglayer->xspacing + (int)(bgtravelled * (1-fglayer->xratio) * fglayer->bgspeedratio)%width;
18202 if(level->scrolldir & SCROLL_UP) {
18203 z = (int) (4 + videomodes.vRes + (advancey + 4) * fglayer->zratio - fglayer->zoffset -
18204 height * fglayer->zrepeat + height + fglayer->zspacing);
18205 } else {
18206 z = (int) (4 + fglayer->zoffset + (advancey - 4) * fglayer->zratio - advancey);
18209 if(x < 0) {
18210 i = (-x) / width;
18211 x %= width;
18212 } else
18213 i = 0;
18214 if(z < 0) {
18215 j = (-z) / height;
18216 z %= height;
18217 } else
18218 j = 0;
18221 screenmethod = plainmethod;
18222 screenmethod.table =
18223 (pixelformat == PIXEL_x8) ? (current_palette >
18224 0 ? (level->palettes[current_palette - 1]) : NULL) : NULL;
18225 screenmethod.alpha = fglayer->alpha;
18226 screenmethod.transbg = fglayer->transparency;
18227 for(; j < fglayer->zrepeat && z < videomodes.vRes; z += height, j++) {
18228 for(k = i, l = x; k < fglayer->xrepeat && l < videomodes.hRes + fglayer->amplitude * 2;
18229 l += width, k++) {
18230 spriteq_add_frame(l, z, FRONTPANEL_Z + fglayer->z, fglayer->sprite, &screenmethod, 0);
18240 void draw_scrolled_bg() {
18241 int i = 0;
18242 int inta;
18243 int poop = 0;
18244 int index = 0;
18245 int fix_y = 0;
18246 unsigned char neonp[32]; //3*8
18247 static float oldadvx = 0, oldadvy = 0;
18248 static int oldpal = 0;
18249 static int neon_count = 0;
18250 static int rockpos = 0;
18251 static const char rockoffssine[32] = {
18252 2, 2, 3, 4, 5, 6, 7, 7,
18253 8, 8, 9, 9, 9, 9, 8, 8,
18254 7, 7, 6, 5, 4, 3, 2, 2,
18255 1, 1, 0, 0, 0, 0, 1, 1
18256 }; // normal rock
18257 static const char rockoffsshake[32] = {
18258 2, 2, 2, 2, 2, 2, 2, 2,
18259 2, 2, 0, 4, 2, 0, 4, 2,
18260 2, 2, 2, 2, 2, 2, 2, 2,
18261 2, 2, 0, 4, 2, 0, 4, 2,
18262 }; // slow, constant jarring rock, like on a train
18263 static const char rockoffsrumble[32] = {
18264 2, 2, 3, 3, 2, 2, 3, 3,
18265 2, 2, 3, 3, 2, 3, 2, 3,
18266 2, 2, 3, 3, 2, 2, 3, 3,
18267 2, 2, 3, 3, 2, 3, 2, 3,
18268 }; // fast, constant rumbling, like in/on a van or trailer
18269 s_screen *pbgscreen;
18270 s_drawmethod screenmethod = plainmethod, *pscreenmethod = &screenmethod;
18271 int pb = pixelbytes[(int) screenformat];
18273 if(pixelformat == PIXEL_x8) {
18274 screenmethod.table = current_palette ? level->palettes[current_palette - 1] : NULL;
18277 if(bgbuffer) {
18278 if(((level->rocking || level->bgspeed > 0 || texture) && !pause) ||
18279 oldadvx != advancex || oldadvy != advancey || current_palette != oldpal)
18280 bgbuffer_updated = 0;
18281 oldadvx = advancex;
18282 oldadvy = advancey;
18283 oldpal = current_palette;
18284 } else
18285 bgbuffer_updated = 0;
18286 //font_printf(2, 100, 1, 0, "%d", bgbuffer_updated);
18288 if(bgbuffer)
18289 pbgscreen = bgbuffer_updated ? vscreen : bgbuffer;
18290 else
18291 pbgscreen = vscreen;
18293 if(!bgbuffer_updated && level->numbglayers > 0)
18294 applybglayers(pbgscreen);
18295 applyfglayers(pbgscreen);
18297 // Append bg with texture?
18298 if(texture) {
18299 inta = (int) (advancex / 2);
18300 if(level->rocking) {
18301 inta += (borTime / (GAME_SPEED / 30));
18302 apply_texture_plane(pbgscreen, 0, background->height, vscreen->width,
18303 BGHEIGHT - background->height, inta * 256, 10, texture, pscreenmethod);
18304 } else
18305 apply_texture_wave(pbgscreen, 0, background->height, vscreen->width,
18306 BGHEIGHT - background->height, inta, 0, texture, borTime, 5, pscreenmethod);
18309 pscreenmethod->alpha = 0;
18310 pscreenmethod->transbg = 0;
18312 if(bgbuffer) {
18313 putscreen(vscreen, bgbuffer, 0, 0, NULL);
18316 bgbuffer_updated = 1;
18318 if(level->rocking) {
18319 rockpos = (borTime / (GAME_SPEED / 8)) & 31;
18320 if(level->rocking == 1)
18321 gfx_y_offset = level->quake - 4 - rockoffssine[rockpos];
18322 else if(level->rocking == 2)
18323 gfx_y_offset = level->quake - 4 - rockoffsshake[rockpos];
18324 else if(level->rocking == 3)
18325 gfx_y_offset = level->quake - 4 - rockoffsrumble[rockpos];
18326 } else if(borTime) {
18327 if(level->quake >= 0)
18328 gfx_y_offset = level->quake - 4;
18329 else
18330 gfx_y_offset = level->quake + 4;
18333 if(level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN)
18334 gfx_y_offset -= (int) (advancey - 4);
18336 // Draw 3 layers: screen, normal and neon
18337 if(panels_loaded && panel_width) {
18338 if(borTime >= neon_time && !freezeall) { // Added freezeall so neon lights don't update if animations are frozen
18339 if(pixelformat == PIXEL_8) // under 8bit mode just cycle the palette from 128 to 135
18341 for(i = 0; i < 8; i++)
18342 neontable[128 + i] = 128 + ((i + neon_count) & 7);
18343 } else if(pixelformat == PIXEL_x8) // copy palette under 24bit mode
18345 if(pscreenmethod->table) {
18346 memcpy(neonp, pscreenmethod->table + 128 * pb, 8 * pb);
18347 memcpy(pscreenmethod->table + 128 * pb, neonp + 2 * pb, 6 * pb);
18348 memcpy(pscreenmethod->table + (128 + 6) * pb, neonp, 2 * pb);
18349 } else {
18350 memcpy(neonp, neontable + 128 * pb, 8 * pb);
18351 memcpy(neontable + 128 * pb, neonp + 2 * pb, 6 * pb);
18352 memcpy(neontable + (128 + 6) * pb, neonp, 2 * pb);
18355 neon_time = borTime + (GAME_SPEED / 3);
18356 neon_count += 2;
18359 if(level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN)
18360 inta = 0;
18361 else
18362 inta = (int) advancex;
18364 poop = inta / panel_width;
18365 inta %= panel_width;
18366 for(i = -inta; i <= videomodes.hRes && poop >= 0 && poop < level->numpanels; i += panel_width) {
18367 index = level->order[poop];
18368 pscreenmethod->table = (pixelformat == PIXEL_x8
18369 && current_palette) ? level->palettes[current_palette - 1] : NULL;
18370 if(panels[index].sprite_normal) {
18371 pscreenmethod->alpha = 0;
18372 spriteq_add_frame(i, gfx_y_offset, PANEL_Z, panels[index].sprite_normal, pscreenmethod,
18375 if(panels[index].sprite_neon) {
18376 if(pixelformat != PIXEL_x8 || current_palette <= 0)
18377 pscreenmethod->table = neontable;
18378 spriteq_add_frame(i, gfx_y_offset, NEONPANEL_Z, panels[index].sprite_neon,
18379 pscreenmethod, 0);
18381 if(panels[index].sprite_screen) {
18382 pscreenmethod->alpha = BLEND_SCREEN + 1;
18383 spriteq_add_frame(i, gfx_y_offset, SCREENPANEL_Z, panels[index].sprite_screen,
18384 pscreenmethod, 0);
18386 poop++;
18390 pscreenmethod->alpha = 0;
18392 for(i = 0; i < level->numholes; i++)
18393 spriteq_add_sprite((int) (level->holes[i].x - advancex),
18394 (int) (level->holes[i].z - level->holes[i].depth + 4 + gfx_y_offset), HOLE_Z,
18395 holesprite, pscreenmethod, 0);
18397 if(frontpanels_loaded) {
18399 if(level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN)
18400 inta = 0;
18401 else {
18402 inta = (int) (advancex * 1.4);
18403 fix_y = (int) (advancey - 4);
18405 poop = inta / frontpanels[0]->width;
18406 inta %= frontpanels[0]->width;
18407 for(i = -inta; i <= videomodes.hRes; i += frontpanels[0]->width) {
18408 poop %= frontpanels_loaded;
18409 spriteq_add_frame(i, gfx_y_offset + fix_y, FRONTPANEL_Z, frontpanels[poop], pscreenmethod, 0);
18410 poop++;
18414 if(level->quake != 0 && borTime >= level->quaketime) {
18415 level->quake /= 2;
18416 level->quaketime = borTime + (GAME_SPEED / 25);
18420 void inputrefresh() {
18421 int p;
18422 int moviestop = 0;
18423 if(movieplay) {
18424 control_update(playercontrolpointers, maxplayers[current_set]);
18425 if(quit_game)
18426 leave_game();
18427 for(p = 0; p < maxplayers[current_set]; p++) {
18428 if(playercontrolpointers[p]->newkeyflags & FLAG_ESC) {
18429 moviestop = 1;
18430 break;
18433 if(!moviestop) {
18434 movie_update(playercontrolpointers);
18435 font_printf(2, 2, 1, 0, "Playing movie, frames: %d/%d",
18436 movieloglen + moviebufptr - MOVIEBUF_LEN, movielen);
18437 } else {
18438 movie_closefile();
18440 } else {
18441 control_update(playercontrolpointers, maxplayers[current_set]);
18442 if(quit_game)
18443 leave_game();
18444 interval = timer_getinterval(GAME_SPEED); // so interval can be logged into movie
18445 if(interval > GAME_SPEED)
18446 interval = GAME_SPEED / GAME_SPEED;
18447 if(interval > GAME_SPEED / 4)
18448 interval = GAME_SPEED / 4;
18451 if(movielog && !pause) {
18452 movie_save(playercontrolpointers);
18453 font_printf(2, 2, 1, 0, "Recording movie, frames: %d", movieloglen + moviebufptr);
18456 bothkeys = 0;
18457 bothnewkeys = 0;
18459 for(p = 0; p < maxplayers[current_set]; p++) {
18460 player[p].releasekeys =
18461 (playercontrolpointers[p]->keyflags | player[p].keys) - playercontrolpointers[p]->keyflags;
18462 player[p].keys = playercontrolpointers[p]->keyflags;
18463 player[p].newkeys = playercontrolpointers[p]->newkeyflags;
18464 player[p].playkeys |= player[p].newkeys;
18465 player[p].playkeys &= player[p].keys;
18467 bothkeys |= player[p].keys;
18468 bothnewkeys |= player[p].newkeys;
18469 if(movielog && (bothnewkeys & FLAG_ESC) && !pause) {
18470 movie_flushbuf();
18471 movie_closefile();
18476 void execute_keyscripts() {
18477 int p;
18478 for(p = 0; p < maxplayers[current_set]; p++) {
18479 if(!pause && (level || selectScreen)
18480 && (player[p].newkeys || (keyscriptrate && player[p].keys) || player[p].releasekeys)) {
18481 if(level) {
18482 execute_level_key_script(p);
18483 if(player[p].ent)
18484 execute_entity_key_script(player[p].ent);
18486 execute_key_script(p);
18487 execute_key_script_all(p);
18492 static void runscript(Script* script) {
18493 Script *ptempscript = pcurrentscript;
18494 if(Script_IsInitialized(script)) {
18495 Script_Execute(script);
18497 pcurrentscript = ptempscript;
18500 void execute_updatescripts() {
18501 runscript(&game_scripts.update_script);
18502 if(level) runscript(&(level->update_script));
18505 void execute_updatedscripts() {
18506 runscript(&game_scripts.updated_script);
18507 if(level) runscript(&(level->updated_script));
18510 void draw_textobjs() {
18511 int i;
18512 s_textobj *textobj;
18513 for(i = 0; i < LEVEL_MAX_TEXTOBJS; i++) {
18514 textobj = level->textobjs + i;
18516 if(textobj->t && textobj->t <= borTime) //If a time was set and passed, remove the text object.
18518 level->textobjs[i].t = 0;
18519 level->textobjs[i].x = 0;
18520 level->textobjs[i].y = 0;
18521 level->textobjs[i].font = 0;
18522 level->textobjs[i].z = 0;
18523 freeAndNull((void**) &level->textobjs[i].text);
18524 } else {
18525 if(textobj->text)
18526 font_printf(textobj->x, textobj->y, textobj->font, textobj->z, textobj->text);
18531 void update(int ingame, int usevwait) {
18532 newtime = 0;
18533 inputrefresh();
18535 if(!pause) {
18536 if(ingame == 1)
18537 execute_updatescripts();
18538 if(ingame == 1 || selectScreen)
18539 execute_keyscripts();
18541 if((level_completed && !level->noslow && !tospeedup) || slowmotion[0]) {
18542 if(slowmotion[1] == slowmotion[2])
18543 newtime = borTime + interval;
18544 } else
18545 newtime = borTime + interval;
18547 slowmotion[2]++;
18548 if(slowmotion[2] == (slowmotion[1] + 1)) {
18549 slowmotion[2] = 0;
18550 if(slowmotion[0] > 1)
18551 slowmotion[1] = slowmotion[0];
18553 if(newtime > borTime + 100)
18554 newtime = borTime + 100;
18556 while(borTime < newtime) {
18557 if(ingame == 1) {
18558 update_scroller();
18559 if(!freezeall) {
18560 if(level->settime > 0
18561 || (!player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent)) {
18562 if(timeleft > 0)
18563 --timeleft;
18564 else if((level->settime > 0 && !player[0].joining && !player[1].joining
18565 && !player[2].joining && !player[3].joining)
18567 (((!noshare && credits < 1)
18568 || (noshare && player[0].credits < 1 && player[1].credits < 1
18569 && player[2].credits < 1 && player[3].credits < 1))
18570 && !player[0].joining && !player[1].joining
18571 && !player[2].joining && !player[3].joining)
18573 time_over();
18578 if(ingame || selectScreen)
18579 update_ents();
18580 ++borTime;
18585 if(ingame == 1 &&
18586 !movieplay &&
18587 !pause && !nopause &&
18588 ((player[0].ent && (player[0].newkeys & FLAG_START)) ||
18589 (player[1].ent && (player[1].newkeys & FLAG_START)) ||
18590 (player[2].ent && (player[2].newkeys & FLAG_START)) || (player[3].ent && (player[3].newkeys & FLAG_START)))
18592 sound_play_sample(samples.beep2, 0, savedata.effectvol, savedata.effectvol, 100);
18593 sound_pause_music(1);
18594 spriteq_lock();
18595 pausemenu();
18598 // gfx section
18599 if(ingame == 1) {
18600 draw_scrolled_bg();
18601 predrawstatus();
18602 drawstatus();
18603 execute_updatedscripts();
18604 draw_textobjs();
18605 } else {
18606 clearscreen(vscreen);
18607 if(background)
18608 putscreen(vscreen, background, 0, 0, NULL);
18610 if(ingame == 1 || selectScreen)
18611 display_ents();
18613 spriteq_draw(vscreen, (ingame == 0)); // notice, always draw sprites at the very end of other methods
18615 if(pause != 2 && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT))
18616 screenshot(vscreen, getpal, 1);
18618 // Debug stuff, should not appear on screenshot
18619 if(debug_time == 0xFFFFFFFF)
18620 debug_time = borTime + GAME_SPEED * 5;
18621 if(borTime < debug_time && debug_msg[0]) {
18622 spriteq_clear();
18623 font_printf(0, 230, 0, 0, debug_msg);
18624 spriteq_draw(vscreen, (ingame == 0));
18625 } else {
18626 debug_msg[0] = 0;
18627 #ifdef DEBUG_MODE
18628 if(level->pos)
18629 debug_printf("Position: %i, width: %i, spawn: %i, offsets: %i/%i", level->pos, level->width,
18630 current_spawn, level->quake, gfx_y_offset);
18631 #endif
18634 if(usevwait)
18635 vga_vwait();
18636 video_copy_screen(vscreen);
18638 spriteq_clear();
18640 check_music();
18641 sound_update_music();
18647 // ----------------------------------------------------------------------
18648 /* Plombo 9/4/2010: New function that can use brightness/gamma correction
18649 * independent from the global palette on platforms where it's available.
18650 * Hardware accelerated brightness/gamma correction is available on Wii and
18651 * OpenGL platforms using TEV and GLSL, respectively. Returns 1 on success, 0 on
18652 * error. */
18653 int set_color_correction(int gm, int br) {
18654 if(opengl) {
18655 vga_set_color_correction(gm, br);
18656 return 1;
18657 } else if(screenformat == PIXEL_8) {
18658 palette_set_corrected(pal, savedata.gamma, savedata.gamma, savedata.gamma, savedata.brightness,
18659 savedata.brightness, savedata.brightness);
18660 return 1;
18661 } else
18662 return 0;
18665 // copied from palette.c, seems it works well
18666 static void _fade_screen(s_screen * screen, int gr, int gg, int gb, int br, int bg, int bb) {
18667 int i, len = screen->width * screen->height;
18668 int pb = pixelbytes[(int) screenformat];
18669 unsigned c, r, g, b;
18671 int_min_max(&gr, -255, 255);
18672 int_min_max(&gg, -255, 255);
18673 int_min_max(&gb, -255, 255);
18674 int_min_max(&br, -255, 255);
18675 int_min_max(&bg, -255, 255);
18676 int_min_max(&bb, -255, 255);
18678 if(pb == 2)
18679 for(i = 0; i < len; i++) {
18680 c = ((unsigned short *) screen->data)[i];
18681 b = (c >> 11) * 0xFF / 0x1F;
18682 g = ((c & 0x7E0) >> 5) * 0xFF / 0x3F;
18683 r = (c & 0x1F) * 0xFF / 0x1F;
18684 ((unsigned short *) screen->data)[i] =
18685 colour16(gbcorrect(r, gr, br), gbcorrect(g, gg, bg), gbcorrect(b, gb, bb));
18687 else
18688 for(i = 0; i < len; i++) {
18689 screen->data[i * pb] = gbcorrect(screen->data[i * pb], gr, br);
18690 screen->data[i * pb + 1] = gbcorrect(screen->data[i * pb + 1], gg, bg);
18691 screen->data[i * pb + 2] = gbcorrect(screen->data[i * pb + 2], gb, bb);
18695 // Simple palette fade / vscreen fade
18696 void fade_out(int type, int speed) {
18697 int i, j = 0;
18698 int b, g = 0;
18699 u32 interval = 0;
18700 unsigned char *thepal = NULL;
18701 int current = speed ? speed : fade;
18703 if(pixelformat == PIXEL_8) {
18704 if(current_palette && level)
18705 thepal = level->palettes[current_palette - 1];
18706 else
18707 thepal = pal;
18710 for(i = 0, j = 0; j < 64;) {
18711 while(j <= i) {
18712 if(!type || type == 1) {
18713 b = ((savedata.brightness + 256) * (64 - j) / 64) - 256;
18714 g = 256 - ((savedata.gamma + 256) * (64 - j) / 64);
18715 vga_vwait();
18716 if(!set_color_correction(g, b))
18717 _fade_screen(vscreen, g, savedata.gamma, savedata.gamma, b, b, b);
18719 j++;
18720 if(!type || type == 1) {
18721 video_copy_screen(vscreen);
18724 if(!type || type == 2) {
18725 sound_update_music();
18726 if(!musicoverlap)
18727 sound_volume_music(savedata.musicvol * (64 - j) / 64,
18728 savedata.musicvol * (64 - j) / 64);
18730 interval = timer_getinterval(current);
18731 if(interval > current)
18732 interval = current / 60;
18733 if(interval > current / 4)
18734 interval = current / 4;
18735 i += interval;
18738 if(!type || type == 2) {
18739 if(!musicoverlap)
18740 sound_close_music();
18743 if(!type || type == 1) {
18744 clearscreen(vscreen);
18745 video_copy_screen(vscreen);
18746 vga_vwait();
18747 //the black screen, so we return to normal palette
18748 set_color_correction(savedata.gamma, savedata.brightness);
18754 void apply_controls() {
18755 int p;
18757 for(p = 0; p < 4; p++) {
18758 control_setkey(playercontrolpointers[p], FLAG_ESC, CONTROL_ESC);
18759 control_setkey(playercontrolpointers[p], FLAG_MOVEUP, savedata.keys[p][SDID_MOVEUP]);
18760 control_setkey(playercontrolpointers[p], FLAG_MOVEDOWN, savedata.keys[p][SDID_MOVEDOWN]);
18761 control_setkey(playercontrolpointers[p], FLAG_MOVELEFT, savedata.keys[p][SDID_MOVELEFT]);
18762 control_setkey(playercontrolpointers[p], FLAG_MOVERIGHT, savedata.keys[p][SDID_MOVERIGHT]);
18763 control_setkey(playercontrolpointers[p], FLAG_ATTACK, savedata.keys[p][SDID_ATTACK]);
18764 control_setkey(playercontrolpointers[p], FLAG_ATTACK2, savedata.keys[p][SDID_ATTACK2]);
18765 control_setkey(playercontrolpointers[p], FLAG_ATTACK3, savedata.keys[p][SDID_ATTACK3]);
18766 control_setkey(playercontrolpointers[p], FLAG_ATTACK4, savedata.keys[p][SDID_ATTACK4]);
18767 control_setkey(playercontrolpointers[p], FLAG_JUMP, savedata.keys[p][SDID_JUMP]);
18768 control_setkey(playercontrolpointers[p], FLAG_SPECIAL, savedata.keys[p][SDID_SPECIAL]);
18769 control_setkey(playercontrolpointers[p], FLAG_START, savedata.keys[p][SDID_START]);
18770 control_setkey(playercontrolpointers[p], FLAG_SCREENSHOT, savedata.keys[p][SDID_SCREENSHOT]);
18776 // ----------------------------------------------------------------------
18778 void display_credits() {
18779 u32 finishtime = borTime + 10 * GAME_SPEED;
18780 int done = 0;
18782 if(savedata.logo != 1)
18783 return;
18784 fade_out(0, 0);
18785 unload_background();
18787 bothnewkeys = 0;
18789 while(!done) {
18790 font_printf(videomodes.hShift + 140, videomodes.vShift + 3, 2, 0, "Credits");
18791 font_printf(videomodes.hShift + 125, videomodes.vShift + 25, 1, 0, "Beats Of Rage");
18792 font_printf(videomodes.hShift + 133, videomodes.vShift + 35, 0, 0, "Senile Team");
18794 font_printf(videomodes.hShift + 138, videomodes.vShift + 55, 1, 0, "OpenBOR");
18795 font_printf(videomodes.hShift + 150, videomodes.vShift + 65, 0, 0, "SX");
18796 font_printf(videomodes.hShift + 70, videomodes.vShift + 75, 0, 0, "CGRemakes");
18797 font_printf(videomodes.hShift + 205, videomodes.vShift + 75, 0, 0, "Fugue");
18798 font_printf(videomodes.hShift + 70, videomodes.vShift + 85, 0, 0, "uTunnels");
18799 font_printf(videomodes.hShift + 205, videomodes.vShift + 85, 0, 0, "Kirby");
18800 font_printf(videomodes.hShift + 70, videomodes.vShift + 95, 0, 0, "LordBall");
18801 font_printf(videomodes.hShift + 205, videomodes.vShift + 95, 0, 0, "Tails");
18802 font_printf(videomodes.hShift + 70, videomodes.vShift + 105, 0, 0, "KBAndressen");
18803 font_printf(videomodes.hShift + 205, videomodes.vShift + 105, 0, 0, "Damon Caskey");
18804 font_printf(videomodes.hShift + 70, videomodes.vShift + 115, 0, 0, "Plombo");
18805 font_printf(videomodes.hShift + 205, videomodes.vShift + 115, 0, 0, "Orochi_X");
18807 font_printf(videomodes.hShift + 138, videomodes.vShift + 125, 1, 0, "Consoles");
18808 font_printf(videomodes.hShift + 70, videomodes.vShift + 135, 0, 0, "PSP, PS3, Linux, OSX");
18809 font_printf(videomodes.hShift + 205, videomodes.vShift + 135, 0, 0, "SX");
18810 font_printf(videomodes.hShift + 70, videomodes.vShift + 145, 0, 0, "Dingoo");
18811 font_printf(videomodes.hShift + 205, videomodes.vShift + 145, 0, 0, "Shin-NiL");
18812 font_printf(videomodes.hShift + 70, videomodes.vShift + 155, 0, 0, "Windows");
18813 font_printf(videomodes.hShift + 205, videomodes.vShift + 155, 0, 0, "SX & Nazo");
18814 font_printf(videomodes.hShift + 70, videomodes.vShift + 165, 0, 0, "GamePark");
18815 font_printf(videomodes.hShift + 205, videomodes.vShift + 165, 0, 0, "SX & Lemon");
18816 font_printf(videomodes.hShift + 70, videomodes.vShift + 175, 0, 0, "DreamCast");
18817 font_printf(videomodes.hShift + 205, videomodes.vShift + 175, 0, 0, "SX & Neill Corlett");
18818 font_printf(videomodes.hShift + 70, videomodes.vShift + 185, 0, 0, "MS XBoX");
18819 font_printf(videomodes.hShift + 205, videomodes.vShift + 185, 0, 0, "SX & XPort");
18820 font_printf(videomodes.hShift + 70, videomodes.vShift + 195, 0, 0, "Wii");
18821 font_printf(videomodes.hShift + 205, videomodes.vShift + 195, 0, 0, "SX & Plombo");
18823 font_printf(videomodes.hShift + 133, videomodes.vShift + 215, 1, 0, "Menu Design");
18824 font_printf(videomodes.hShift + 70, videomodes.vShift + 225, 0, 0, "SX");
18825 font_printf(videomodes.hShift + 205, videomodes.vShift + 225, 0, 0, "Fightn Words");
18827 update(2, 0);
18829 done |= (borTime > finishtime);
18830 done |= (bothnewkeys & (FLAG_START + FLAG_ESC));
18832 fade = 75;
18833 fade_out(0, 0);
18837 void borShutdown(const char *caller, int status, char *msg, ...) {
18838 PLOG("shutdown called from %s\n", caller);
18839 char buf[1024] = "";
18840 va_list arglist;
18842 va_start(arglist, msg);
18843 vsprintf(buf, msg, arglist);
18844 va_end(arglist);
18846 switch (status) {
18847 case 0:
18848 PLOG("\n************ Shutting Down ************\n\n");
18849 break;
18850 default:
18851 PLOG("\n********** An Error Occurred **********"
18852 "\n* Shutting Down *\n\n");
18853 break;
18856 PLOG("%s", buf);
18858 savesettings();
18860 if(status != 2) ; //display_credits();
18861 if(startup_done)
18862 term_videomodes();
18864 PLOG("Release level data");
18865 if(startup_done)
18866 unload_levelorder();
18867 PLOG("...........");
18868 if(startup_done)
18869 unload_level();
18870 PLOG("\tDone!\n");
18872 PLOG("Release graphics data");
18873 PLOG("..");
18874 if(startup_done)
18875 freescreen(&vscreen); // allocated by init_videomodes
18876 PLOG("..");
18877 if(startup_done)
18878 freescreen(&background);
18879 PLOG("..");
18881 if(startup_done)
18882 freesprites();
18883 PLOG("..");
18884 if(startup_done)
18885 unload_all_fonts();
18886 PLOG("\tDone!\n");
18889 PLOG("Release game data............\n\n");
18891 if(startup_done)
18892 free_ents();
18893 if(startup_done)
18894 free_models();
18895 if(startup_done)
18896 free_modelcache();
18897 if(startup_done)
18898 clear_scripts();
18899 PLOG("\nRelease game data............\tDone!\n");
18901 PLOG("Release timer................");
18902 if(startup_done)
18903 borTimerExit();
18904 PLOG("\tDone!\n");
18906 PLOG("Release input hardware.......");
18907 if(startup_done)
18908 control_exit();
18909 PLOG("\tDone!\n");
18911 PLOG("Release sound system.........");
18912 if(startup_done)
18913 sound_exit();
18914 PLOG("\tDone!\n");
18916 PLOG("Release FileCaching System...");
18917 if(startup_done)
18918 pak_term();
18919 PLOG("\tDone!\n");
18921 if(modelcmdlist)
18922 freeCommandList(modelcmdlist); // moved here because list is not initialized if shutdown is initiated from inside the menu
18923 if(modelsattackcmdlist)
18924 freeCommandList(modelsattackcmdlist);
18925 if(modelstxtcmdlist)
18926 freeCommandList(modelstxtcmdlist);
18927 if(levelcmdlist)
18928 freeCommandList(levelcmdlist);
18929 if(levelordercmdlist)
18930 freeCommandList(levelordercmdlist);
18931 if(scriptConstantsCommandList)
18932 freeCommandList(scriptConstantsCommandList);
18934 freeModelList();
18936 freefilenamecache();
18938 PLOG("\n**************** Done *****************\n\n");
18940 #ifdef DEBUG
18941 assert(status == 0); // this way we can haz backtrace.
18942 #endif
18944 exit(status);
18947 void startup() {
18948 int i;
18950 printf("FileCaching System Init......\t");
18951 if(pak_init())
18952 printf("Enabled\n");
18953 else
18954 printf("Disabled\n");
18956 loadHighScoreFile();
18957 clearSavedGame();
18960 if(!init_videomodes())
18961 shutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes);
18963 printf("Loading menu.txt.............\t");
18964 load_menu_txt();
18965 printf("Done!\n");
18967 printf("Loading fonts................\t");
18968 load_all_fonts();
18969 printf("Done!\n");
18971 printf("Timer init...................\t");
18972 borTimerInit();
18973 printf("Done!\n");
18975 printf("Initialize Sound..............\t");
18976 if(savedata.usesound && sound_init(12)) {
18977 if(load_special_sounds())
18978 printf("Done!\n");
18979 else
18980 printf("\n");
18981 if(!sound_start_playback(savedata.soundbits, savedata.soundrate))
18982 printf("Warning: can't play sound at %u Hz!\n", savedata.soundrate);
18983 SB_setvolume(SB_MASTERVOL, 15);
18984 SB_setvolume(SB_VOICEVOL, savedata.soundvol);
18985 } else
18986 shutdown(1, "Unable to Initialize Sound.\n");
18988 printf("Loading sprites..............\t");
18989 load_special_sprites();
18990 printf("Done!\n");
18992 printf("Loading level order..........\t");
18993 load_levelorder();
18994 printf("Done!\n");
18996 printf("Loading script settings......\t");
18997 load_script_setting();
18998 printf("Done!\n");
19000 printf("Loading scripts..............\t");
19001 load_scripts();
19002 printf("Done!\n");
19004 printf("Loading models...............\n\n");
19005 load_models();
19007 printf("Object engine init...........\t");
19008 if(!alloc_ents())
19009 shutdown(1, (char*) E_OUT_OF_MEMORY);
19010 printf("Done!\n");
19012 printf("Input init...................\t");
19013 control_init(savedata.usejoy);
19014 apply_controls();
19015 printf("Done!\n");
19017 printf("\n\n");
19019 for(i = 0; i < MAX_PAL_SIZE / 4; i++)
19020 neontable[i] = i;
19021 if(savedata.logo++ > 10)
19022 savedata.logo = 0;
19023 savesettings();
19024 startup_done = 1;
19028 // ----------------------------------------------------------------------------
19030 // Returns 0 on error, -1 on escape
19031 int playgif(char *filename, int x, int y, int noskip) {
19032 unsigned char gifpal[768] = { 0 };
19033 int code;
19034 int delay;
19035 u32 milliseconds;
19036 u32 nextframe;
19037 u32 lasttime;
19038 u32 temptime, tempnewtime; // temporary patch for ingame gif play
19039 int done;
19040 int frame = 0;
19041 int synctosound = 0;
19043 s_screen *tempbg = background;
19044 background = allocscreen(videomodes.hRes, videomodes.vRes, pixelformat);
19045 if(background == NULL)
19046 shutdown(1, (char*) E_OUT_OF_MEMORY);
19047 clearscreen(background);
19048 standard_palette(1);
19050 if(!anigif_open(filename, packfile, background->palette ? background->palette : gifpal)) {
19051 freescreen(&background);
19052 background = tempbg;
19053 return 0;
19056 temptime = borTime;
19057 tempnewtime = newtime;
19058 borTime = 0;
19059 lasttime = 0;
19060 milliseconds = 0;
19061 nextframe = 0;
19062 delay = 100;
19063 code = ANIGIF_DECODE_RETRY;
19064 done = 0;
19065 synctosound = (sound_getinterval() != 0xFFFFFFFF);
19067 while(!done) {
19068 if(milliseconds >= nextframe) {
19069 if(code != ANIGIF_DECODE_END) {
19070 while((code = anigif_decode(background, &delay, x, y)) == ANIGIF_DECODE_RETRY) ;
19071 // if(code == ANIGIF_DECODE_FRAME){
19072 // Set time for next frame
19073 nextframe += delay * 10;
19074 // }
19075 } else
19076 done = 1;
19078 if(code == ANIGIF_DECODE_END)
19079 break;
19081 if(frame == 0) {
19082 vga_vwait();
19083 if(!background->palette) {
19084 palette_set_corrected(gifpal, savedata.gamma, savedata.gamma, savedata.gamma,
19085 savedata.brightness, savedata.brightness, savedata.brightness);
19087 update(0, 0);
19088 } else
19089 update(0, 1);
19091 ++frame;
19093 if(synctosound) {
19094 milliseconds += sound_getinterval();
19095 if(milliseconds == 0xFFFFFFFF)
19096 synctosound = 0;
19098 if(!synctosound)
19099 milliseconds += (borTime - lasttime) * 1000 / GAME_SPEED;
19100 lasttime = borTime;
19102 if(!noskip && (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)))
19103 done = 1;
19105 anigif_close();
19107 borTime = temptime;
19108 newtime = tempnewtime;
19110 freescreen(&background);
19111 background = tempbg;
19112 if(bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON))
19113 return -1;
19114 return 1;
19118 void playscene(char *filename) {
19119 char *buf;
19120 size_t size;
19121 int pos;
19122 char *command = NULL;
19123 char giffile[256];
19124 int x = 0, y = 0, skipone = 0, noskip = 0;
19125 int closing = 0;
19127 ArgList arglist;
19128 char argbuf[MAX_ARG_LEN + 1] = "";
19130 // Read file
19131 if(buffer_pakfile(filename, &buf, &size) != 1)
19132 return;
19134 // Now interpret the contents of buf line by line
19135 pos = 0;
19136 while(buf[pos]) {
19137 ParseArgs(&arglist, buf + pos, argbuf);
19138 command = GET_ARG(0);
19139 if(command[0]) {
19140 if(!closing && stricmp(command, "music") == 0) {
19141 music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
19142 } else if(!closing && stricmp(command, "animation") == 0) {
19143 strcpy(giffile, GET_ARG(1));
19144 x = GET_INT_ARG(2);
19145 y = GET_INT_ARG(3);
19146 skipone = GET_INT_ARG(4);
19147 noskip = GET_INT_ARG(5);
19148 if(playgif(giffile, x, y, noskip) == -1 && !skipone)
19149 closing = 1;
19150 } else if(stricmp(command, "silence") == 0) {
19151 sound_close_music();
19154 // Go to next non-blank line
19155 pos += getNewLineStart(buf + pos);
19157 freeAndNull((void**) &buf);
19160 // ----------------------------------------------------------------------------
19162 void gameover(void) {
19163 int done = 0;
19164 int playback_started = 0;
19166 music("data/music/gameover", 0, 0);
19168 borTime = 0;
19169 while(!done) {
19170 if(!playback_started && testpackfile("data/scenes/gameover.txt", packfile) >= 0)
19171 playscene("data/scenes/gameover.txt");
19172 else
19173 font_printf(_strmidx(3, "GAME OVER"), 110 + videomodes.vShift, 3, 0, "GAME OVER");
19174 playback_started = 1;
19175 done |= (borTime > GAME_SPEED * 8 && !sound_query_music(NULL, NULL));
19176 done |= (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON));
19177 update(0, 0);
19182 void hallfame(int addtoscore) {
19183 int done = 0;
19184 int topten[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
19185 u32 score;
19186 char name[MAX_NAME_LEN + 1];
19187 int i, p, y;
19188 char tmpBuff[128] = { "" };
19189 s_model *model = NULL;
19190 int col1 = -8;
19191 int col2 = 6;
19193 if(hiscorebg) {
19194 // New alternative background path for PSP
19195 if(custBkgrds != NULL) {
19196 strcpy(tmpBuff, custBkgrds);
19197 strncat(tmpBuff, "hiscore", 7);
19198 load_background(tmpBuff, 0);
19199 } else
19200 load_cached_background("data/bgs/hiscore", 0);
19203 if(addtoscore) {
19204 for(p = 0; p < maxplayers[current_set]; p++) {
19205 model = findmodel(player[p].name);
19206 if(player[p].score > savescore.highsc[9]) {
19207 savescore.highsc[9] = player[p].score;
19208 strcpy(savescore.hscoren[9], model->name);
19209 topten[9] = 1;
19211 for(i = 8; i >= 0 && player[p].score > savescore.highsc[i]; i--) {
19212 score = savescore.highsc[i];
19213 strcpy(name, savescore.hscoren[i]);
19214 savescore.highsc[i] = player[p].score;
19215 strcpy(savescore.hscoren[i], model->name);
19216 topten[i] = 1;
19217 savescore.highsc[i + 1] = score;
19218 strcpy(savescore.hscoren[i + 1], name);
19219 topten[i + 1] = 0;
19225 borTime = 0;
19227 while(!done) {
19228 y = 60;
19229 if(!hiscorebg)
19230 font_printf(_strmidx(3, "Hall Of Fame"), y - font_heights[3] - 10 + videomodes.vShift, 3, 0,
19231 "Hall Of Fame");
19233 for(i = 0; i < 10; i++) {
19234 font_printf(_colx(topten[i], col1), y + videomodes.vShift, topten[i], 0, "%2i. %s", i + 1,
19235 savescore.hscoren[i]);
19236 font_printf(_colx(topten[i], col2), y + videomodes.vShift, topten[i], 0,
19237 (scoreformat ? "%09lu" : "%u"), savescore.highsc[i]);
19238 y += font_heights[topten[i]] + 6;
19241 update(0, 0);
19242 done |= (borTime > GAME_SPEED * 8);
19243 done |= (bothnewkeys & (FLAG_START + FLAG_ESC));
19245 unload_background();
19248 // Level completed, show bonus stuff
19249 void showcomplete(int num) {
19250 int done = 0;
19251 int i, j, k;
19252 u32 clearbonus[4] = { 10000, 10000, 10000, 10000 };
19253 u32 lifebonus[4] = { 10000, 10000, 10000, 10000 };
19254 u32 rushbonus[4] = { 10000, 10000, 10000, 10000 };
19255 u32 nexttime = 0;
19256 u32 finishtime = 0;
19257 int chan = 0;
19258 char tmpBuff[128] = { "" };
19260 if(completebg) {
19261 // New alternative background path for PSP
19262 if(custBkgrds != NULL) {
19263 strcpy(tmpBuff, custBkgrds);
19264 strncat(tmpBuff, "complete", 8);
19265 load_background(tmpBuff, 0);
19266 } else
19267 load_cached_background("data/bgs/complete", 0);
19270 music("data/music/complete", 0, 0);
19272 for(i = 0; i < maxplayers[current_set]; i++) {
19273 if(rush[0] >= 1 && showrushbonus == 1) {
19274 rushbonus[i] = nomaxrushreset[i] * scbonuses[2];
19276 if(scbonuses[3] == 1)
19277 clearbonus[i] = num * scbonuses[0];
19278 else
19279 clearbonus[i] = scbonuses[0];
19280 lifebonus[i] = player[i].lives * scbonuses[1];
19283 update(0, 0);
19285 borTime = 0;
19286 while(!done) {
19287 if(!scomplete[5])
19288 font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0,
19289 "Stage %i Complete!", num);
19290 else {
19291 font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, "Stage");
19292 font_printf(videomodes.hShift + scomplete[2], videomodes.vShift + scomplete[3], 3, 0, "%i",
19293 num);
19294 font_printf(videomodes.hShift + scomplete[4], videomodes.vShift + scomplete[5], 3, 0,
19295 "Complete");
19298 font_printf(videomodes.hShift + cbonus[0], videomodes.vShift + cbonus[1], 0, 0, "Clear Bonus");
19299 for(i = 0, j = 2, k = 3; i < maxplayers[current_set]; i++, j = j + 2, k = k + 2)
19300 if(player[i].lives > 0)
19301 font_printf(videomodes.hShift + cbonus[j], videomodes.vShift + cbonus[k], 0, 0,
19302 (scoreformat ? "%09lu" : "%lu"), clearbonus[i]);
19303 font_printf(videomodes.hShift + lbonus[0], videomodes.vShift + lbonus[1], 0, 0, "Life bonus");
19304 for(i = 0, j = 2, k = 3; i < maxplayers[current_set]; i++, j = j + 2, k = k + 2)
19305 if(player[i].lives > 0)
19306 font_printf(videomodes.hShift + lbonus[j], videomodes.vShift + lbonus[k], 0, 0,
19307 (scoreformat ? "%09lu" : "%lu"), lifebonus[i]);
19308 if(rush[0] >= 1 && showrushbonus == 1) {
19309 font_printf(videomodes.hShift + rbonus[0], videomodes.vShift + rbonus[1], 0, 0, "Rush Bonus");
19310 for(i = 0, j = 2, k = 3; i < maxplayers[current_set]; i++, j = j + 2, k = k + 2)
19311 if(player[i].lives > 0)
19312 font_printf(videomodes.hShift + rbonus[j], videomodes.vShift + rbonus[k], 0, 0,
19313 (scoreformat ? "%09lu" : "%lu"), rushbonus[i]);
19315 font_printf(videomodes.hShift + tscore[0], videomodes.vShift + tscore[1], 0, 0, "Total Score");
19316 for(i = 0, j = 2, k = 3; i < maxplayers[current_set]; i++, j = j + 2, k = k + 2)
19317 if(player[i].lives > 0)
19318 font_printf(videomodes.hShift + tscore[j], videomodes.vShift + tscore[k], 0, 0,
19319 (scoreformat ? "%09lu" : "%lu"), player[i].score);
19321 while(borTime > nexttime) {
19322 if(!finishtime)
19323 finishtime = borTime + 4 * GAME_SPEED;
19325 for(i = 0; i < maxplayers[current_set]; i++) {
19326 if(player[i].lives > 0) {
19327 if(clearbonus[i] > 0) {
19328 addscore(i, 10);
19329 clearbonus[i] -= 10;
19330 finishtime = 0;
19331 } else if(lifebonus[i] > 0) {
19332 addscore(i, 10);
19333 lifebonus[i] -= 10;
19334 finishtime = 0;
19335 } else if(rush[0] >= 1 && showrushbonus == 1 && (rushbonus[i] > 0)) {
19336 addscore(i, 10);
19337 rushbonus[i] -= 10;
19338 finishtime = 0;
19343 if(!finishtime && !(nexttime & 15)) {
19344 sound_stop_sample(chan);
19345 chan =
19346 sound_play_sample(samples.beep, 0, savedata.effectvol / 2, savedata.effectvol / 2,
19347 100);
19349 nexttime++;
19352 if(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC))
19353 done = 1;
19354 if(finishtime && borTime > finishtime)
19355 done = 1;
19357 update(0, 0);
19360 // Add remainder of score, incase player skips counter
19361 for(i = 0; i < maxplayers[current_set]; i++) {
19362 if(player[i].lives > 0) {
19363 if(rush[0] >= 1 && showrushbonus == 1) {
19364 addscore(i, rushbonus[i]);
19366 addscore(i, clearbonus[i]);
19367 addscore(i, lifebonus[i]);
19370 unload_background();
19373 void savelevelinfo() {
19374 int i;
19375 savelevel[current_set].flag = cansave_flag[current_set];
19376 // don't check flag here save all info, for simple logic
19377 for(i = 0; i < maxplayers[current_set]; i++) {
19378 savelevel[current_set].pLives[i] = player[i].lives;
19379 savelevel[current_set].pCredits[i] = player[i].credits;
19380 savelevel[current_set].pScores[i] = player[i].score;
19381 savelevel[current_set].pSpawnhealth[i] = player[i].spawnhealth;
19382 savelevel[current_set].pSpawnmp[i] = player[i].spawnmp;
19383 savelevel[current_set].pWeapnum[i] = player[i].weapnum;
19384 savelevel[current_set].pColourmap[i] = player[i].colourmap;
19385 strncpy(savelevel[current_set].pName[i], player[i].name, MAX_NAME_LEN);
19387 savelevel[current_set].credits = credits;
19388 savelevel[current_set].level = current_level;
19389 savelevel[current_set].stage = current_stage;
19390 savelevel[current_set].which_set = current_set;
19391 strncpy(savelevel[current_set].dName, set_names[current_set], MAX_NAME_LEN);
19396 int playlevel(char *filename) {
19397 int i;
19398 Script *ptempscript = pcurrentscript;
19400 kill_all();
19402 savelevelinfo();
19403 saveGameFile();
19404 saveHighScoreFile();
19405 saveScriptFile();
19407 load_level(filename);
19408 borTime = 0;
19410 // Fixes the start level executing last button bug
19411 for(i = 0; i < maxplayers[current_set]; i++) {
19412 if(player[i].lives > 0) {
19413 player[i].newkeys = player[i].playkeys = 0;
19414 player[i].weapnum = level->setweap;
19415 spawnplayer(i);
19416 player[i].ent->rush[1] = 0;
19420 //execute a script when level started
19421 if(Script_IsInitialized(&game_scripts.level_script))
19422 Script_Execute(&game_scripts.level_script);
19423 if(Script_IsInitialized(&(level->level_script)))
19424 Script_Execute(&(level->level_script));
19426 while(!endgame) {
19427 update(1, 0);
19428 if(level_completed)
19429 endgame |= (!findent(TYPE_ENEMY) || level->type || findent(TYPE_ENDLEVEL)); // Ends when all enemies die or a bonus level
19431 //execute a script when level finished
19432 if(Script_IsInitialized(&game_scripts.endlevel_script))
19433 Script_Execute(&game_scripts.endlevel_script);
19434 if(Script_IsInitialized(&(level->endlevel_script)))
19435 Script_Execute(&(level->endlevel_script));
19436 fade_out(0, 0);
19438 for(i = 0; i < maxplayers[current_set]; i++) {
19439 if(player[i].ent) {
19440 nomaxrushreset[i] = player[i].ent->rush[1];
19441 player[i].spawnhealth = player[i].ent->health;
19442 player[i].spawnmp = player[i].ent->mp;
19446 if(!musicoverlap)
19447 sound_close_music();
19449 kill_all();
19450 unload_level();
19452 pcurrentscript = ptempscript;
19454 return (player[0].lives > 0 || player[1].lives > 0 || player[2].lives > 0 || player[3].lives > 0); //4player
19458 int selectplayer(int *players, char *filename) {
19459 s_model *tempmodel;
19460 entity *example[4] = { NULL, NULL, NULL, NULL };
19461 int i, x;
19462 int cmap[MAX_PLAYERS] = { 0, 1, 2, 3 };
19463 int tperror = 0;
19464 int exit = 0;
19465 int ready[MAX_PLAYERS] = { 0, 0, 0, 0 };
19466 int escape = 0;
19467 int players_busy = 0;
19468 int players_ready = 0;
19469 int immediate[MAX_PLAYERS] = { 0, 0, 0, 0 };
19470 char string[128] = { "" };
19471 char *buf, *command;
19472 size_t size = 0;
19473 unsigned line = 1;
19474 ptrdiff_t pos = 0;
19475 ArgList arglist;
19476 char argbuf[MAX_ARG_LEN + 1] = "";
19478 selectScreen = 1;
19479 kill_all();
19480 reset_playable_list(1);
19482 if(loadGameFile()) {
19483 bonus = 0;
19484 for(i = 0; i < MAX_DIFFICULTIES; i++)
19485 if(savelevel[i].times_completed > 0)
19486 bonus += savelevel[i].times_completed;
19489 if(filename && filename[0]) {
19490 if(buffer_pakfile(filename, &buf, &size) != 1)
19491 shutdown(1, "Failed to load player select file '%s'", filename);
19492 while(pos < size) {
19493 ParseArgs(&arglist, buf + pos, argbuf);
19494 command = GET_ARG(0);
19495 if(command && command[0]) {
19496 if(stricmp(command, "music") == 0) {
19497 music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3)));
19498 } else if(stricmp(command, "allowselect") == 0) {
19499 load_playable_list(buf + pos);
19500 } else if(stricmp(command, "background") == 0) {
19501 load_background(GET_ARG(1), 1);
19502 } else if(stricmp(command, "load") == 0) {
19503 tempmodel = findmodel(GET_ARG(1));
19504 if(!tempmodel)
19505 load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2));
19506 else
19507 update_model_loadflag(tempmodel, GET_INT_ARG(2));
19508 } else if(command && command[0])
19509 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
19512 pos += getNewLineStart(buf + pos);
19513 line++;
19515 freeAndNull((void**) &buf);
19516 for(i = 0; i < maxplayers[current_set]; i++) {
19517 if(players[i]) {
19518 if(!psmenu[i][0] && !psmenu[i][1]) {
19519 if(maxplayers[current_set] > 2)
19520 example[i] =
19521 spawn((float)
19522 ((111 - (maxplayers[current_set] * 14)) +
19523 ((i * (320 - (166 / maxplayers[current_set])) /
19524 maxplayers[current_set]) + videomodes.hShift)),
19525 (float) (230 + videomodes.vShift), 0, spdirection[i], NULL,
19526 -1, nextplayermodel(NULL));
19527 else
19528 example[i] =
19529 spawn((float)
19530 (83 + (videomodes.hShift / 2) +
19531 (i * (155 + videomodes.hShift))),
19532 (float) (230 + videomodes.vShift), 0, spdirection[i], NULL,
19533 -1, nextplayermodel(NULL));
19534 } else
19535 example[i] =
19536 spawn((float) psmenu[i][0], (float) psmenu[i][1], 0, spdirection[i], NULL,
19537 -1, nextplayermodel(NULL));
19540 } else // without select.txt
19542 if(skipselect && (*skipselect)[current_set][0]) {
19543 for(i = 0; i < MAX_PLAYERS; i++) {
19544 memset(&player[i], 0, sizeof(s_player));
19545 if(!players[i])
19546 continue;
19548 if((*skipselect)[current_set][i]) // just in case or it will be an array overflow issue
19549 strncpy(player[i].name, (*skipselect)[current_set][i], MAX_NAME_LEN);
19550 //else continue;
19551 if(!noshare)
19552 credits = CONTINUES;
19553 else {
19554 player[i].credits = CONTINUES;
19555 player[i].hasplayed = 1;
19557 if(!creditscheat) {
19558 if(noshare)
19559 --player[i].credits;
19560 else
19561 --credits;
19563 player[i].lives = PLAYER_LIVES;
19565 selectScreen = 0;
19566 return 1;
19569 if(unlockbg && bonus) {
19570 // New alternative background path for PSP
19571 if(custBkgrds != NULL) {
19572 strcpy(string, custBkgrds);
19573 strncat(string, "unlockbg", 8);
19574 load_background(string, 1);
19575 } else
19576 load_cached_background("data/bgs/unlockbg", 1);
19577 } else {
19578 // New alternative background path for PSP
19579 if(custBkgrds != NULL) {
19580 strncpy(string, custBkgrds, 128);
19581 strncat(string, "select", 6);
19582 load_background(string, 1);
19583 } else
19584 load_cached_background("data/bgs/select", 1);
19586 if(!music("data/music/menu", 1, 0))
19587 music("data/music/remix", 1, 0);
19588 if(!noshare)
19589 credits = CONTINUES;
19590 for(i = 0; i < MAX_PLAYERS; i++) {
19591 memset(&player[i], 0, sizeof(s_player));
19592 immediate[i] = players[i];
19597 while(!(exit || escape)) {
19598 players_busy = 0;
19599 players_ready = 0;
19600 for(i = 0; i < maxplayers[current_set]; i++) {
19601 // you can't have that character!
19602 if((tperror == i + 1) && !ready[i])
19603 font_printf(75 + videomodes.hShift, 123 + videomodes.vShift, 0, 0,
19604 "Player %d Choose a Different Character!", i + 1);
19605 if(!ready[i]) {
19606 if(player[i].lives <= 0 && (noshare || credits > 0)
19607 && ((player[i].newkeys & FLAG_ANYBUTTON) || immediate[i])) {
19608 if(noshare) {
19609 player[i].credits = CONTINUES;
19610 player[i].hasplayed = 1;
19613 if(!creditscheat) {
19614 if(noshare)
19615 --player[i].credits;
19616 else
19617 --credits;
19620 player[i].lives = PLAYER_LIVES;
19622 if(!psmenu[i][0] && !psmenu[i][1]) {
19623 if(maxplayers[current_set] > 2)
19624 example[i] =
19625 spawn((float)
19626 ((111 - (maxplayers[current_set] * 14)) +
19627 ((i * (320 - (166 / maxplayers[current_set])) /
19628 maxplayers[current_set]) + videomodes.hShift)),
19629 (float) (230 + videomodes.vShift), 0, spdirection[i],
19630 NULL, -1, nextplayermodel(NULL));
19631 else
19632 example[i] =
19633 spawn((float)
19634 (83 + (videomodes.hShift / 2) +
19635 (i * (155 + videomodes.hShift))),
19636 (float) (230 + videomodes.vShift), 0, spdirection[i],
19637 NULL, -1, nextplayermodel(NULL));
19638 } else
19639 example[i] =
19640 spawn((float) psmenu[i][0], (float) psmenu[i][1], 0, spdirection[i],
19641 NULL, -1, nextplayermodel(NULL));
19643 if(example[i] == NULL)
19644 shutdown(1, "Failed to create player selection object!");
19646 // Select Player Direction for select player screen
19647 // example[i]->direction = spdirection[i]; // moved to spawn method
19649 // Make player 2 different colour automatically
19650 player[i].colourmap = i;
19652 while((example[i]->modeldata.hmap1) && (example[i]->modeldata.hmap2) &&
19653 cmap[i] >= example[i]->modeldata.hmap1 &&
19654 cmap[i] <= example[i]->modeldata.hmap2) {
19655 cmap[i]++;
19656 if(cmap[i] > example[i]->modeldata.maps_loaded)
19657 cmap[i] = 0;
19660 player[i].playkeys = 0;
19661 ent_set_colourmap(example[i], cmap[i]);
19663 sound_play_sample(samples.beep, 0, savedata.effectvol,
19664 savedata.effectvol, 100);
19665 } else if(player[i].newkeys & FLAG_MOVELEFT && example[i]) {
19666 sound_play_sample(samples.beep, 0, savedata.effectvol,
19667 savedata.effectvol, 100);
19668 ent_set_model(example[i], prevplayermodel(example[i]->model)->name);
19669 cmap[i] = i;
19671 while((example[i]->modeldata.hmap1) && (example[i]->modeldata.hmap2) &&
19672 cmap[i] >= example[i]->modeldata.hmap1 &&
19673 cmap[i] <= example[i]->modeldata.hmap2) {
19674 cmap[i]++;
19675 if(cmap[i] > example[i]->modeldata.maps_loaded)
19676 cmap[i] = 0;
19679 ent_set_colourmap(example[i], cmap[i]);
19680 tperror = 0;
19681 } else if(player[i].newkeys & FLAG_MOVERIGHT && example[i]) {
19682 sound_play_sample(samples.beep, 0, savedata.effectvol,
19683 savedata.effectvol, 100);
19684 ent_set_model(example[i], nextplayermodel(example[i]->model)->name);
19685 cmap[i] = i;
19687 while((example[i]->modeldata.hmap1) && (example[i]->modeldata.hmap2) &&
19688 cmap[i] >= example[i]->modeldata.hmap1
19689 && cmap[i] <= example[i]->modeldata.hmap2) {
19690 cmap[i]++;
19691 if(cmap[i] > example[i]->modeldata.maps_loaded)
19692 cmap[i] = 0;
19695 ent_set_colourmap(example[i], cmap[i]);
19696 tperror = 0;
19698 // oooh pretty colors! - selectable color scheme for player characters
19699 else if(player[i].newkeys & FLAG_MOVEUP && colourselect && example[i]) {
19700 do {
19701 cmap[i]++;
19702 if(cmap[i] > example[i]->modeldata.maps_loaded)
19703 cmap[i] = 0;
19705 while((example[i]->modeldata.fmap &&
19706 cmap[i] - 1 == example[i]->modeldata.fmap - 1) ||
19707 ((example[i]->modeldata.hmap1) && (example[i]->modeldata.hmap2) &&
19708 cmap[i] - 1 >= example[i]->modeldata.hmap1 - 1 &&
19709 cmap[i] - 1 <= example[i]->modeldata.hmap2 - 1)
19712 ent_set_colourmap(example[i], cmap[i]);
19713 } else if(player[i].newkeys & FLAG_MOVEDOWN && colourselect && example[i]) {
19714 do {
19715 cmap[i]--;
19716 if(cmap[i] < 0)
19717 cmap[i] = example[i]->modeldata.maps_loaded;
19719 while((example[i]->modeldata.fmap &&
19720 cmap[i] - 1 == example[i]->modeldata.fmap - 1) ||
19721 ((example[i]->modeldata.hmap1) && (example[i]->modeldata.hmap2) &&
19722 cmap[i] - 1 >= example[i]->modeldata.hmap1 - 1 &&
19723 cmap[i] - 1 <= example[i]->modeldata.hmap2 - 1)
19726 ent_set_colourmap(example[i], cmap[i]);
19727 } else if((player[i].newkeys & FLAG_ANYBUTTON) && example[i]) {
19728 sound_play_sample(samples.beep2, 0, savedata.effectvol,
19729 savedata.effectvol, 100);
19730 strcpy(player[i].name, example[i]->modeldata.name);
19731 player[i].colourmap = cmap[i];
19733 // reports error if players try to use the same character and sameplay mode is off
19734 if(sameplayer) {
19735 for(x = 0; x < maxplayers[current_set]; x++) {
19736 if((i != x) && (!strcmp(player[i].name, player[x].name))) {
19737 tperror = i + 1;
19738 break;
19743 if(!tperror) {
19744 borTime = 0;
19745 // yay you picked me!
19746 if(validanim(example[i], ANI_PICK))
19747 ent_set_anim(example[i], ANI_PICK, 0);
19748 while(!ready[i] && example[i] != NULL) {
19749 update(0, 0);
19750 if((!validanim(example[i], ANI_PICK)
19751 || example[i]->modeldata.animation[ANI_PICK]->loop[0])
19752 && borTime > GAME_SPEED * 2)
19753 ready[i] = 1;
19754 else if(!example[i]->animating)
19755 ready[i] = 1;
19756 if(ready[i])
19757 borTime = 0;
19761 } else {
19762 if(!psmenu[i][2] && !psmenu[i][3]) {
19763 if(maxplayers[current_set] > 2)
19764 font_printf((95 - (maxplayers[current_set] * 14)) +
19765 ((i * (320 - (166 / maxplayers[current_set])) /
19766 maxplayers[current_set]) + videomodes.hShift),
19767 225 + videomodes.vShift, 0, 0, "Ready!");
19768 else
19769 font_printf(67 + (videomodes.hShift / 2) +
19770 (i * (155 + videomodes.hShift)), 225 + videomodes.vShift, 0,
19771 0, "Ready!");
19772 } else
19773 font_printf(psmenu[i][2], psmenu[i][3], 0, 0, "Ready!");
19776 if(example[i] != NULL)
19777 players_busy++;
19778 if(ready[i])
19779 players_ready++;
19782 if(players_busy && players_busy == players_ready && borTime > GAME_SPEED)
19783 exit = 1;
19784 update(0, 0);
19786 if(bothnewkeys & FLAG_ESC)
19787 escape = 1;
19790 // No longer at the select screen
19791 selectScreen = 0;
19792 kill_all();
19793 sound_close_music();
19795 return (!escape);
19798 void playgame(int *players, unsigned which_set, int useSavedGame) {
19799 int i;
19800 current_level = 0;
19801 current_stage = 1;
19802 current_set = which_set;
19804 if(which_set >= num_difficulties)
19805 return;
19806 // shutdown(1, "Illegal set chosen: index %i (there are only %i sets)!", which_set, num_difficulties);
19808 allow_secret_chars = ifcomplete[which_set];
19809 PLAYER_LIVES = difflives[which_set];
19810 musicoverlap = diffoverlap[which_set];
19811 fade = custfade[which_set];
19812 CONTINUES = diffcreds[which_set];
19813 magic_type = typemp[which_set];
19814 if(PLAYER_LIVES == 0)
19815 PLAYER_LIVES = 3;
19816 if(CONTINUES == 0)
19817 CONTINUES = 5;
19818 if(fade == 0)
19819 fade = 24;
19820 sameplayer = same[which_set];
19822 memset(player, 0, sizeof(s_player) * 4);
19824 if(useSavedGame) {
19825 loadScriptFile();
19826 current_level = savelevel[current_set].level;
19827 current_stage = savelevel[current_set].stage;
19828 if(savelevel[current_set].flag == 2) // don't check 1 or 0 becuase if we use saved game the flag must be >0
19830 for(i = 0; i < maxplayers[current_set]; i++) {
19831 player[i].lives = savelevel[current_set].pLives[i];
19832 player[i].score = savelevel[current_set].pScores[i];
19833 player[i].colourmap = savelevel[current_set].pColourmap[i];
19834 player[i].weapnum = savelevel[current_set].pWeapnum[i];
19835 player[i].spawnhealth = savelevel[current_set].pSpawnhealth[i];
19836 player[i].spawnmp = savelevel[current_set].pSpawnmp[i];
19837 strncpy(player[i].name, savelevel[current_set].pName[i], MAX_NAME_LEN);
19839 credits = savelevel[current_set].credits;
19841 } else {
19842 max_global_var_index = 0;
19845 if((useSavedGame && savelevel[current_set].flag == 2) || selectplayer(players, NULL)) // if save flag is 2 don't select player
19847 while(current_level < num_levels[which_set]) {
19848 if(branch_name[0]) // branch checking
19850 //current_stage = 1; //jump, jump... perhaps we don't need to reset it, modders should take care of it.
19851 for(i = 0; i < num_levels[which_set]; i++) {
19852 if(stricmp(levelorder[which_set][i]->branchname, branch_name) == 0) {
19853 current_level = i;
19854 branch_name[0] = 0; // clear up so we won't stuck here
19855 break;
19857 //if(levelorder[which_set][i]->gonext==1) ++current_stage; OX. Commented this line out. Seems to be cause of inacurate stage # complete message.
19860 PLAYER_MIN_Z = levelorder[which_set][current_level]->z_coords[0];
19861 PLAYER_MAX_Z = levelorder[which_set][current_level]->z_coords[1];
19862 BGHEIGHT = levelorder[which_set][current_level]->z_coords[2];
19864 if(levelorder[which_set][current_level]->type == cut_scene)
19865 playscene(levelorder[which_set][current_level]->filename);
19866 else if(levelorder[which_set][current_level]->type == select_screen) {
19867 for(i = 0; i < MAX_PLAYERS; i++)
19868 players[i] = (player[i].lives > 0);
19869 if(selectplayer(players, levelorder[which_set][current_level]->filename) == 0) {
19870 break;
19872 } else if(!playlevel(levelorder[which_set][current_level]->filename)) {
19873 if(player[0].lives <= 0 && player[1].lives <= 0 && player[2].lives <= 0
19874 && player[3].lives <= 0) {
19875 gameover();
19876 if(!noshowhof[which_set])
19877 hallfame(1);
19878 for(i = 0; i < maxplayers[current_set]; i++) {
19879 player[i].hasplayed = 0;
19880 player[i].weapnum = 0;
19883 break;
19885 if(levelorder[which_set][current_level]->gonext == 1) {
19886 showcomplete(current_stage);
19887 for(i = 0; i < maxplayers[current_set]; i++) {
19888 player[i].spawnhealth = 0;
19889 player[i].spawnmp = 0;
19891 ++current_stage;
19892 savelevel[current_set].stage = current_stage;
19894 current_level++;
19895 savelevel[current_set].level = current_level;
19896 //2007-2-24, gonext = 2, end game
19897 if(levelorder[which_set][current_level - 1]->gonext == 2) {
19898 current_level = num_levels[which_set];
19902 if(current_level >= num_levels[which_set]) {
19903 bonus += savelevel[current_set].times_completed++;
19904 saveGameFile();
19905 saveHighScoreFile();
19906 fade_out(0, 0);
19907 hallfame(1);
19910 // clear global script variant list
19911 max_global_var_index = -1;
19912 for(i = 0; i < max_indexed_vars; i++)
19913 ScriptVariant_Clear(indexed_var_list + i);
19914 sound_close_music();
19917 void term_videomodes() {
19918 videomodes.hRes = 0;
19919 videomodes.vRes = 0;
19920 video_set_mode(videomodes);
19921 freeAndNull((void**) &custBkgrds);
19922 freeAndNull((void**) &custLevels);
19923 freeAndNull((void**) &custModels);
19926 // Load Video Mode from file
19927 int init_videomodes(void) {
19928 int result;
19929 char *filename = "data/video.txt";
19930 int bits = 8, tmp;
19931 ptrdiff_t pos;
19932 size_t size;
19933 char *buf = NULL;
19934 char *command = NULL;
19935 char *value = NULL;
19936 ArgList arglist;
19937 char argbuf[MAX_ARG_LEN + 1] = "";
19938 char lowercase_buf[16];
19939 unsigned i, line = 1;
19941 printf("Initializing video............\n");
19943 // 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.
19944 #define tryfile(X) if((tmp=openpackfile(X,packfile))!=-1) { closepackfile(tmp); filename=X; goto readfile; }
19945 tryfile("data/videopc.txt");
19946 #undef tryfile
19948 readfile:
19949 // Read file
19950 if(buffer_pakfile(filename, &buf, &size) != 1) {
19951 videoMode = VTM_320_240;
19952 printf("'%s' not found.\n", filename);
19953 goto VIDEOMODES;
19956 printf("Reading video settings from '%s'.\n", filename);
19958 // Now interpret the contents of buf line by line
19959 pos = 0;
19960 while(pos < size) {
19961 ParseArgs(&arglist, buf + pos, argbuf);
19962 command = GET_ARG(0);
19964 if(command && command[0]) {
19965 char_to_lower(lowercase_buf, command, sizeof(lowercase_buf));
19966 for(i = 0 ; i < VTC_MAX; i++) {
19967 if(!strcmp(lowercase_buf, video_txt_commands_strings[i])) {
19968 if(video_txt_commands_dest[i])
19969 *video_txt_commands_dest[i] = strdup(GET_ARG(1));
19970 else if(i == VTC_VIDEO)
19971 videoMode = GET_INT_ARG(1);
19972 else if(i == VTC_COLOURDEPTH) {
19973 pixelformat = PIXEL_x8;
19974 value = GET_ARG(1);
19975 if(stricmp(value, "8bit") == 0) {
19976 screenformat = PIXEL_8;
19977 pixelformat = PIXEL_8;
19978 } else if(stricmp(value, "16bit") == 0) {
19979 screenformat = PIXEL_16;
19980 bits = 16;
19981 } else if(stricmp(value, "32bit") == 0) {
19982 screenformat = PIXEL_32;
19983 bits = 32;
19984 } else if(value[0] == 0)
19985 screenformat = PIXEL_32;
19986 else
19987 shutdown(1, "Screen colour depth can only be either 8bit, 16bit or 32bit.");
19989 break;
19992 if(i == VTC_MAX)
19993 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__, command, filename, line);
19995 // Go to next line
19996 pos += getNewLineStart(buf + pos);
19997 line++;
19999 freeAndNull((void**) &buf);
20001 VIDEOMODES:
20002 if(videoMode >= VTM_MAX)
20003 shutdown(1,
20004 "Invalid video mode: %d in 'data/video.txt', supported modes:\n"
20005 "0 - 320x240\n" "1 - 480x272\n" "2 - 640x480\n" "3 - 720x480\n"
20006 "4 - 800x480\n" "5 - 800x600\n" "6 - 960x540\n\n", videoMode);
20008 videomodes = videomodes_init_data[videoMode];
20009 videomodes.mode = savedata.screen[videoMode][0];
20010 videomodes.filter = savedata.screen[videoMode][1];
20011 player_min_max_z_bgheight = player_min_max_z_bgheight_init_data[videoMode];
20013 video_stretch(savedata.stretch);
20015 if((vscreen = allocscreen(videomodes.hRes, videomodes.vRes, screenformat)) == NULL)
20016 shutdown(1, (char*) E_OUT_OF_MEMORY);
20017 videomodes.pixel = pixelbytes[(int) vscreen->pixelformat];
20018 result = video_set_mode(videomodes);
20020 if(result) {
20021 clearscreen(vscreen);
20022 printf("Initialized video.............\t%dx%d (Mode: %d, Depth: %d Bit)\n\n", videomodes.hRes,
20023 videomodes.vRes, videoMode, bits);
20025 return result;
20028 // ----------------------------------------------------------------------------
20030 // Set key or button safely (with switching)
20031 void safe_set(int *arr, int index, int newkey, int oldkey) {
20032 int i;
20033 for(i = 0; i < 12; i++) {
20034 if(arr[i] == newkey)
20035 arr[i] = oldkey;
20037 arr[index] = newkey;
20040 void keyboard_setup(int player_nr) {
20041 int quit = 0;
20042 printf("Loading control settings.......\t");
20044 savesettings();
20045 bothnewkeys = 0;
20047 controller_options(player_nr, &quit, (char**) &buttonnames, disabledkey);
20049 if(quit == 2) {
20050 apply_controls();
20051 savesettings();
20052 } else
20053 loadsettings();
20055 update(0, 0);
20056 printf("Done!\n");
20061 // ----------------------------------------------------------------------------
20063 void openborMain(int argc, char **argv) {
20064 sprite_map = NULL;
20065 //int quit = 0;
20066 int relback = 1;
20067 u32 introtime = 0;
20068 int started = 0;
20069 char tmpBuff[128] = { "" };
20070 int players[MAX_PLAYERS];
20071 int argl;
20073 printf("OpenBoR %s, Compile Date: " __DATE__ "\n\n", VERSION);
20075 if(argc > 1) {
20076 argl = strlen(argv[1]);
20077 if(argl > 14 && !memcmp(argv[1], "offscreenkill=", 14))
20078 DEFAULT_OFFSCREEN_KILL = getValidInt((char *) argv[1] + 14, "", "");
20079 if(argl > 14 && !memcmp(argv[1], "showfilesused=", 14))
20080 printFileUsageStatistics = getValidInt((char *) argv[1] + 14, "", "");
20083 modelcmdlist = createModelCommandList();
20084 modelstxtcmdlist = createModelstxtCommandList();
20085 modelsattackcmdlist = createModelAttackCommandList();
20086 levelcmdlist = createLevelCommandList();
20087 levelordercmdlist = createLevelOrderCommandList();
20088 scriptConstantsCommandList = createScriptConstantsCommandList();
20089 createModelList();
20091 // Load necessary components.
20092 printf("Game Selected: %s\n\n", packfile);
20093 loadsettings();
20094 startup();
20096 // New alternative background path for PSP
20097 if(custBkgrds != NULL) {
20098 strcpy(tmpBuff, custBkgrds);
20099 strncat(tmpBuff, "logo", 4);
20100 load_background(tmpBuff, 0);
20101 } else {
20102 printf("use cached bg\n");
20103 load_cached_background("data/bgs/logo", 0);
20106 while(borTime < GAME_SPEED * 6 && !(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC)))
20107 update(0, 0);
20109 music("data/music/remix", 1, 0);
20111 playscene("data/scenes/logo.txt");
20113 clearscreen(background);
20115 while(!quit_game) {
20116 if(borTime >= introtime) {
20117 playscene("data/scenes/intro.txt");
20118 update(0, 0);
20119 introtime = borTime + GAME_SPEED * 20;
20120 relback = 1;
20121 started = 0;
20124 if(bothnewkeys & FLAG_ESC)
20125 quit_game = 1;
20127 if(!started) {
20128 if((borTime % GAME_SPEED) < (GAME_SPEED / 2))
20129 _menutextm(0, 0, 0, "PRESS START");
20130 if(bothnewkeys & (FLAG_ANYBUTTON)) {
20131 started = 1;
20132 relback = 1;
20134 update(0, 0);
20135 } else {
20136 relback = main_menu(&started, &introtime, players);
20138 if(relback) {
20139 if(started) {
20140 if(custBkgrds != NULL) {
20141 strncpy(tmpBuff, custBkgrds, 128);
20142 strncat(tmpBuff, "titleb", 6);
20143 load_background(tmpBuff, 0);
20144 } else
20145 load_cached_background("data/bgs/titleb", 0);
20146 } else {
20147 if(custBkgrds != NULL) {
20148 strncpy(tmpBuff, custBkgrds, 128);
20149 strncat(tmpBuff, "title", 5);
20150 load_background(tmpBuff, 0);
20151 } else
20152 load_cached_background("data/bgs/title", 0);
20155 if(!sound_query_music(NULL, NULL))
20156 music("data/music/remix", 1, 0);
20157 relback = 0;
20160 leave_game();
20163 #undef GET_ARG
20164 #undef GET_ARG_LEN
20165 #undef GET_ARGP
20166 #undef GET_ARGP_LEN
20167 #undef GET_INT_ARG
20168 #undef GET_FLOAT_ARG
20169 #undef GET_INT_ARGP
20170 #undef GET_FLOAT_ARGP