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
9 /////////////////////////////////////////////////////////////////////////////
11 // Side-scrolling beat-'em-up //
12 /////////////////////////////////////////////////////////////////////////////
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
= {
56 char custom_button_names
[CB_MAX
][16];
57 char disabledkey
[CB_MAX
] = { 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
97 int current_level
= 0;
98 int current_stage
= 1;
106 float scrolldx
; // advancex changed previous loop
107 float scrolldy
; // advancey .....................
108 float scrollminz
; // Limit level z-scroll
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;
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
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
];
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
165 unsigned char slowmotion
[3] = { 0, 2, 0 }; // [0] = enable/disable; [1] = duration; [2] = counter;
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
173 int current_palette
= 0;
176 int gosound
= 0; // Used to prevent go sound playing too frequently,
177 int musicoverlap
= 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"
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
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.
201 int forcecheatsoff
= 0;
204 int creditscheat
= 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 };
239 int timeloc
[6] = { 0, 0, 0, 0, 0, -1 }; // Used for customizable timeclock location/size
242 short timeicon_offsets
[2] = { 0, 0 };
243 char timeicon_path
[128] = { "" };
246 short bgicon_offsets
[3] = { 0, 0, 0 };
247 char bgicon_path
[128] = { "" };
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;
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 };
298 unsigned int videoMode
= 0;
299 int scoreformat
= 0; // If set fill score values with 6 Zeros
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;
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
];
321 int ent_count
= 0; // log count of entites
323 int combodelay
= GAME_SPEED
/ 2;
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
;
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" \
350 "Special thanks to SEGA and SNK.\n\n", \
353 void leave_game(void) {
354 if(strcmp(packfile
, MENU_PACK_FILENAME
))
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
) {
373 assert(index
< a
->numframes
);
374 a
->drawmethods
[index
] = m
;
377 s_drawmethod
*getDrawMethod(s_anim
* a
, ptrdiff_t index
) {
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
,
396 s
->set
= (LSTYPE_BACKGROUND
| LSTYPE_BAR
);
399 s
->set
= LSTYPE_BACKGROUND
;
405 s
->set
= LSTYPE_NONE
;
408 s
->set
= LSTYPE_NONE
;
409 printf("invalid loadingbg type %d!\n", set
);
417 s
->refreshMs
= (ms
? ms
: 100);
422 // returns: 1 - succeeded 0 - failed
423 int buffer_pakfile(char *filename
, char **pbuffer
, size_t * psize
) {
428 PDEBUG("pakfile requested: %s.\n", filename
); //ASDF
430 if((handle
= openpackfile(filename
, packfile
)) < 0) {
431 PDEBUG("couldnt get handle!\n");
434 *psize
= seekpackfile(handle
, 0, SEEK_END
);
435 seekpackfile(handle
, 0, SEEK_SET
);
437 *pbuffer
= (char *) malloc(*psize
+ 1);
438 if(*pbuffer
== NULL
) {
440 closepackfile(handle
);
441 shutdown(1, "Can't create buffer for packfile '%s'", filename
);
444 if(readpackfile(handle
, *pbuffer
, *psize
) != *psize
) {
445 freeAndNull((void**) pbuffer
);
447 closepackfile(handle
);
448 shutdown(1, "Can't read from packfile '%s'", filename
);
451 (*pbuffer
)[*psize
] = 0; // Terminate string (important!)
452 closepackfile(handle
);
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
) {
506 _e_slowmotion_duration
,
521 case _e_lasthita
: case _e_lasthitx
: case _e_lasthitz
:
522 case _e_xpos
: case _e_ypos
:
523 ScriptVariant_ChangeType(var
, VT_DECIMAL
);
525 case _e_xpos
: case _e_ypos
:
529 var
->dblVal
= (double) advancex
;
531 var
->dblVal
= (double) advancey
;
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;
541 ScriptVariant_ChangeType(var
, VT_STR
);
542 strcpy(StrCache_Get(var
->strVal
), branch_name
);
544 case _e_player
: case _e_player1
: case _e_player2
:
545 case _e_player3
: case _e_player4
:
546 ScriptVariant_ChangeType(var
, VT_PTR
);
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;
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
);
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;
608 case _e_game_speed
: case _e_gfx_y_offset
: case _e_levelwidth
: case _e_levelheight
:
611 ScriptVariant_ChangeType(var
, VT_INTEGER
);
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;
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");
629 // change a system variant, used by script
630 int changesyspropertybyindex(int index
, ScriptVariant
* value
) {
631 //char* tempstr = NULL;
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
{
649 _csv_slowmotion_duration
,
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
, <emp
))) {
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;
683 case _csv_smartbomber
:
684 smartbomber
= (entity
*) value
;
687 textbox
= (entity
*) value
;
697 int load_script(Script
* script
, char *file
) {
702 if(buffer_pakfile(file
, &buf
, &size
) != 1)
705 failed
= !Script_AppendText(script
, buf
, file
);
707 freeAndNull((void**) &buf
);
708 // text loaded but parsing failed, shutdown
710 shutdown(1, "Failed to parse script file: '%s'!\n", file
);
714 // this method is used by load_scripts, don't call it
715 void init_scripts() {
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() {
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() {
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
*);
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
*);
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
*);
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
*);
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
= {
791 .attacker
= "attacker",
793 .type
= "attacktype",
794 .noblock
= "noblock",
795 .guardcost
= "guardcost",
796 .jugglecost
= "jugglecost",
797 .pauseadd
= "pauseadd",
800 .blocked
= "blocked",
801 .animnum
= "animnum",
804 .attacktype
= "attacktype",
808 .obstacle
= "obstacle",
812 .damagetaker
= "damagetaker",
816 static const s_script_args init_script_args_default
= {
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
= {
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
;
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
) {
884 tempvar
.ptrVal
= (void*) tuples
[i
].value
;
887 tempvar
.lVal
= (s32
) tuples
[i
].value
;
890 memcpy(&tmp_float
, &tuples
[i
].value
, sizeof(float));
891 tempvar
.dblVal
= (double) tmp_float
;
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
;
1210 pcurrentscript
= tempnode
->spawn_script
;
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
);
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
) {
1293 FILE *handle
= NULL
;
1294 char path
[128] = { "" };
1295 getBasePath(path
, "Saves", 0);
1296 strncat(path
, dest
, 128);
1297 handle
= fopen(path
, "wb");
1300 disCcWarns
= fwrite(&savedata
, 1, sizeof(s_savedata
), 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) {
1328 FILE *handle
= NULL
;
1330 char path
[256] = { "" };
1331 char tmpname
[256] = { "" };
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");
1343 //global variables count
1344 for(i
= 0, c
= 0; i
<= max_global_var_index
; i
++) {
1345 if(!global_var_list
[i
]->owner
)
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
);
1354 if(max_indexed_vars
<= 0)
1356 disCcWarns
= fwrite(indexed_var_list
+ i
, sizeof(ScriptVariant
), max_indexed_vars
, 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) {
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
))) {
1379 handle
= fopen(path
, "rb");
1382 disCcWarns
= fread(&savedata
, 1, sizeof(s_savedata
), handle
);
1384 if(savedata
.compatibleversion
!= COMPATIBLEVERSION
)
1388 void loadfromdefault(void) {
1390 FILE *handle
= NULL
;
1391 char path
[128] = { "" };
1392 getBasePath(path
, "Saves", 0);
1393 strncat(path
, "default.cfg", 128);
1395 handle
= fopen(path
, "rb");
1398 disCcWarns
= fread(&savedata
, 1, sizeof(s_savedata
), handle
);
1400 if(savedata
.compatibleversion
!= COMPATIBLEVERSION
)
1406 int loadGameFile(void) {
1408 FILE *handle
= NULL
;
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");
1418 disCcWarns
= fread(&savelevel
, sizeof(s_savelevel
), MAX_DIFFICULTIES
, handle
);
1420 for(i
= 0; i
< MAX_DIFFICULTIES
; i
++)
1421 if(savelevel
[i
].compatibleversion
!= CV_SAVED_GAME
)
1426 void loadHighScoreFile(void) {
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
);
1435 handle
= fopen(path
, "rb");
1438 disCcWarns
= fread(&savescore
, 1, sizeof(s_savescore
), handle
);
1440 if(savescore
.compatibleversion
!= CV_HIGH_SCORE
)
1446 void loadScriptFile(void) {
1452 FILE *handle
= NULL
;
1453 char path
[256] = { "" };
1454 char tmpname
[256] = { "" };
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");
1466 fseek(handle
, 0, SEEK_END
);
1467 size
= ftell(handle
);
1468 fseek(handle
, 0, SEEK_SET
);
1469 if(size
< sizeof(c
)) {
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
);
1481 if(max_indexed_vars
<= 0) {
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
);
1492 void clearSavedGame(void) {
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) {
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
) {
1519 if(!savedata
.usemusic
)
1521 if(!sound_open_music(filename
, packfile
, savedata
.musicvol
, loop
, offset
)) {
1522 printf("\nCan't play music file '%s'\n", filename
);
1525 if(savedata
.showtitles
&& sound_query_music(a
, t
)) {
1527 debug_printf("Playing \"%s\" by %s", t
, a
);
1529 debug_printf("Playing unknown song by %s", a
);
1531 debug_printf("Playing \"%s\" by unknown artist", t
);
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
);
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
) {
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";
1589 if(isNumeric(text
)) {
1592 printf(WARN_NUMBER_EXPECTED
, file
, cmd
, text
);
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";
1603 if(isNumeric(text
)) {
1606 printf(WARN_NUMBER_EXPECTED
, file
, cmd
, text
);
1611 size_t ParseArgs(ArgList
* list
, char *input
, char *output
) {
1615 //static const char diff = 'a' - 'A';
1618 size_t wordstart
= 0;
1622 //int makelower = 0;
1624 while(pos
< MAX_ARG_LEN
- 1 && item
< 18) {
1625 switch (input
[pos
]) {
1634 if(!space
&& wordstart
!= pos
) {
1635 list
->args
[item
] = output
+ wordstart
;
1636 list
->arglen
[item
] = pos
- wordstart
;
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':
1649 /*output[pos] = makelower ? input[pos] + diff : input[pos]; */
1650 output
[pos
] = input
[pos
];
1662 char *findarg(char *command
, int which
) {
1663 static const char comment_mark
[4] = { "#" };
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.
1678 while(d
< MAX_ARG_LEN
- 1 && command
[d
]) {
1679 // Zero out whitespace
1680 if(command
[d
] == ' ' || command
[d
] == '\t') {
1684 return arg
+ argstart
;
1685 } else if(command
[d
] == 0 || command
[d
] == '\n' || command
[d
] == '\r' ||
1686 (!strcmp(command
+ d
, comment_mark
))) {
1690 return arg
+ argstart
;
1694 // if(argc==-1 && command[d]=='#') return arg;
1699 arg
[d
] = command
[d
];
1710 float diff(float a
, float b
) {
1718 int inair(entity
* e
) {
1719 return (diff(e
->a
, e
->base
) >= 0.1);
1724 float randf(float max
) {
1728 f
= (float) (rand32() % 1000);
1735 // ----------------------- Loaders ------------------------------
1738 // Creates a remapping table from two images
1739 int load_colourmap(s_model
* model
, char *image1
, char *image2
) {
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)
1749 // Find an empty slot... ;)
1750 for(k
= 0; k
< MAX_COLOUR_MAPS
&& model
->colourmap
[k
]; k
++) ;
1751 if(k
>= MAX_COLOUR_MAPS
)
1754 if((map
= malloc(MAX_PAL_SIZE
/ 4)) == NULL
) {
1757 if((bitmap1
= loadbitmap(image1
, packfile
, PIXEL_8
)) == NULL
) {
1758 freeAndNull((void**) &map
);
1761 if((bitmap2
= loadbitmap(image2
, packfile
, PIXEL_8
)) == NULL
) {
1762 freebitmap(bitmap1
);
1763 freeAndNull((void**) &map
);
1766 // Create the colour map
1767 for(i
= 0; i
< MAX_PAL_SIZE
/ 4; 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;
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
[]) {
1791 unsigned char *newmap
, *oldmap
;
1792 unsigned char *p1
, *p2
;
1793 unsigned pb
= pixelbytes
[(int) screenformat
];
1794 if(model
->palette
== NULL
)
1796 for(c
= 0; c
< model
->maps_loaded
; c
++) {
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
++) {
1808 p1
= newmap
+ i
* pb
;
1809 p2
= model
->palette
+ oldmap
[i
] * pb
;
1812 model
->colourmap
[c
] = newmap
;
1813 freeAndNull((void**) &oldmap
);
1818 static int _load_palette16(unsigned char *palette
, char *filename
) {
1820 unsigned char tp
[3];
1821 handle
= openpackfile(filename
, packfile
);
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
);
1830 ((unsigned short *) palette
)[i
] = colour16(tp
[0], tp
[1], tp
[2]);
1832 closepackfile(handle
);
1833 *(unsigned short *) palette
= 0;
1839 static int _load_palette32(unsigned char *palette
, char *filename
) {
1842 unsigned char tpal
[3];
1843 handle
= openpackfile(filename
, packfile
);
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
);
1853 dp
[i
] = colour32(tpal
[0], tpal
[1], tpal
[2]);
1856 closepackfile(handle
);
1862 //load a 256 colors' palette
1863 int load_palette(unsigned char *palette
, char *filename
) {
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
);
1873 if(readpackfile(handle
, palette
, 768) != 768) {
1874 closepackfile(handle
);
1877 closepackfile(handle
);
1878 palette
[0] = palette
[1] = palette
[2] = 0;
1883 // create blending tables for the palette
1884 int create_blending_tables(unsigned char *palette
, unsigned char *tables
[], int usemap
[]) {
1886 if(pixelformat
!= PIXEL_8
)
1888 if(!palette
|| !tables
)
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
);
1904 //change system palette by index
1905 void change_system_palette(int palindex
) {
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
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);
1936 change_system_palette(0);
1941 void unload_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
) {
1954 return palette_find(pal
, r
, g
, b
);
1956 return colour16(r
, g
, b
);
1958 return colour32(r
, g
, b
);
1963 // parses a color string in the format "R_G_B" or as a raw integer
1964 int parsecolor(const char *string
) {
1966 if(strchr(string
, '_') != strrchr(string
, '_')) { // 2 underscores; color is in "R_G_B" format
1968 g
= atoi(strchr(string
, '_') + 1);
1969 b
= atoi(strrchr(string
, '_') + 1);
1970 return _makecolour(r
, g
, b
);
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";
1982 char argbuf
[MAX_ARG_LEN
+ 1] = "";
1983 char lowercase_buf
[16];
1988 if(buffer_pakfile(filename
, &buf
, &size
) != 1) {
1989 memset(&colors
, 0, sizeof(colors
));
1990 shadowalpha
= BLEND_MULTIPLY
+ 1;
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
,
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));
2055 if(!strcmp(lowercase_buf
, "shadowalpha")) //gfxshadow alpha
2056 shadowalpha
= GET_INT_ARG(1);
2058 printf("Warning: Unknown command in lifebar.txt: '%s'.\n", command
);
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;
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
);
2116 shutdown(1, "Error loading background, Unknown Pixel Format %d, file %s!\n", pixelformat
, filename
);
2120 standard_palette(0);
2121 if(!create_blending_tables(pal
, blendings
, blendfx
))
2122 shutdown(1, (char*) E_OUT_OF_MEMORY
);
2126 for(i
= 0; i
< s_colors_itemcount
; i
++) {
2127 set_color_if_empty(&color_array
[i
], &default_colors_array
[i
]);
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
) {
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
;
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
) {
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
;
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
) {
2202 texture
= loadbitmap(filename
, packfile
, pixelformat
);
2204 shutdown(1, "Error loading file '%s'", filename
);
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
]);
2216 frontpanels_loaded
= 0;
2221 s_sprite
*loadsprite2(char *filename
, int *width
, int *height
) {
2223 s_bitmap
*bitmap
= NULL
;
2224 s_sprite
*sprite
= NULL
;
2225 int clipl
, clipr
, clipt
, clipb
;
2227 bitmap
= loadbitmap(filename
, packfile
, pixelformat
);
2231 *width
= bitmap
->width
;
2233 *height
= bitmap
->height
;
2234 clipbitmap(bitmap
, &clipl
, &clipr
, &clipt
, &clipb
);
2235 size
= fakey_encodesprite(bitmap
);
2236 sprite
= (s_sprite
*) malloc(size
);
2241 encodesprite(-clipl
, -clipt
, bitmap
, sprite
);
2247 s_sprite
*loadpanel2(char *filename
) {
2251 if(NULL
== (sprite
= loadsprite2(filename
, &w
, &h
)))
2256 if(h
> panel_height
)
2262 int loadpanel(s_panel_filenames
* filenames_s
) {
2265 char** filenames
= (char**) filenames_s
;
2266 s_sprite
** sprites
= (s_sprite
**) &panels
[panels_loaded
];
2268 if(panels_loaded
>= MAX_PANELS
)
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;
2282 if(sprites
[i
]->palette
) // under 24bit mode, copy the palette
2283 memcpy(neontable
, sprites
[i
]->palette
, PAL_BYTES
);
2284 } else if (i
== 2) {
2287 blendfx
[BLEND_SCREEN
] = 1;
2293 return 0; // Nothing was loaded!
2300 int loadfrontpanel(char *filename
) {
2303 s_bitmap
*bitmap
= NULL
;
2304 int clipl
, clipr
, clipt
, clipb
;
2307 if(frontpanels_loaded
>= MAX_PANELS
)
2309 bitmap
= loadbitmap(filename
, packfile
, pixelformat
);
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
]) {
2321 encodesprite(-clipl
, -clipt
, bitmap
, frontpanels
[frontpanels_loaded
]);
2324 ++frontpanels_loaded
;
2329 // Added to conserve memory
2330 void resourceCleanUp() {
2334 load_special_sounds();
2335 load_script_setting();
2336 load_special_sprites();
2341 void freesprites() {
2343 s_sprite_list
*head
;
2344 for(i
= 0; i
<= sprites_loaded
; i
++) {
2346 freeAndNull((void**) &sprite_list
->sprite
);
2347 freeAndNull((void**) &sprite_list
->filename
);
2348 head
= sprite_list
->next
;
2353 freeAndNull((void**) &sprite_map
);
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
);
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.
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
) {
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
)
2391 bitmap
= loadbitmap(filename
, packfile
, bmpformat
);
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
;
2404 return sprites_loaded
- 1;
2410 bitmap
= loadbitmap(filename
, packfile
, bmpformat
);
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
) {
2425 shutdown(1, (char*) E_OUT_OF_MEMORY
);
2427 encodesprite(ofsx
- clipl
, ofsy
- clipt
, bitmap
, curr
->sprite
);
2428 if(sprite_list
== NULL
) {
2430 sprite_list
->next
= NULL
;
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
;
2445 return sprites_loaded
- 1;
2448 void load_special_sprites() {
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
,
2458 *special_sprites_init
[i
].target
= -1;
2460 if(timeicon_path
[0])
2461 timeicon
= loadsprite(timeicon_path
, 0, 0, pixelformat
);
2463 bgicon
= loadsprite(bgicon_path
, 0, 0, pixelformat
);
2465 olicon
= loadsprite(olicon_path
, 0, 0, pixelformat
);
2468 void unload_all_fonts() {
2470 for(i
= 0; i
< 8; i
++) {
2474 void load_all_fonts() {
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
);
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];
2496 char *buf
, *command
;
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
2506 ParseArgs(&arglist
, buf
+ pos
, argbuf
);
2507 command
= GET_ARG(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
])) {
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
];
2529 stringcase(menutxt_cmd
, fontmonospace
):
2530 for(i
= 0; i
< 8; i
++)
2531 fontmonospace
[i
] = GET_INT_ARG(i
+1);
2534 if(command
&& command
[0])
2535 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__
, command
, filename
, 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() {
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;
2577 // Use by player select menus
2578 s_model
*nextplayermodel(s_model
* current
) {
2583 // Find index of current player model
2584 for(i
= 0; i
< models_cached
; i
++) {
2585 if(model_cache
[i
].model
== current
) {
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
)
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!");
2604 // Use by player select menus
2605 s_model
*prevplayermodel(s_model
* current
) {
2610 // Find index of current player model
2611 for(i
= 0; i
< models_cached
; i
++) {
2612 if(model_cache
[i
].model
== current
) {
2618 // Find next player model (first one after current index)
2619 for(i
= curindex
- 1, loops
= 0; loops
< models_cached
; i
--, loops
++) {
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!");
2631 // Reset All Player Models to on/off for Select Screen.
2632 static void reset_playable_list(char which
) {
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
) {
2645 s_model
*playermodels
= NULL
;
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
);
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
) {
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
);
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
) {
2702 if(list
->anim
->model_index
== index
) {
2705 free_anim(list
->anim
);
2709 list
->next
= anim_list_delete(list
->next
, index
);
2713 void free_anim(s_anim
* 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
) {
2725 return (m
->freetypes
& t
) == t
;
2728 void addFreeType(s_model
* m
, ModelFreetype t
) {
2733 // Unload single model from memory
2734 int free_model(s_model
* model
) {
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) {
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) {
2812 while((temp
= getFirstModel()))
2815 // free animation ids
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
));
2829 if(anim_list
== NULL
) {
2831 anim_list
->next
= NULL
;
2835 anim_list
->next
= head
;
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
;
2848 alloc_frames(a
, framecount
);
2850 framecount
= -framecount
; // for alloc method, use a negative value
2852 currentframe
= 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])) {
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
));
2886 a
->idle
[currentframe
] = idle
;
2887 if(move
&& !a
->move
) {
2888 a
->move
= calloc(framecount
, sizeof(*a
->move
));
2891 a
->move
[currentframe
] = move
;
2892 if(movez
&& !a
->movez
) {
2893 a
->movez
= calloc(framecount
, sizeof(*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
));
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
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
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
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
) {
2946 char *command
, *value
;
2948 char argbuf
[MAX_ARG_LEN
+ 1] = "";
2951 if(buffer_pakfile(model_cache
[index
].path
, &buf
, &size
) != 1)
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
) {
2962 freeAndNull((void**) &model_cache
[index
].name
);
2963 model_cache
[index
].name
= strdup(value
);
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
);
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
);
3001 void free_modelcache() {
3002 if(model_cache
!= NULL
) {
3003 while(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
) {
3015 for(i
= 0; i
< models_cached
; i
++) {
3016 if(stricmp(name
, model_cache
[i
].name
) == 0)
3022 char *get_cached_model_path(char *name
) {
3024 for(i
= 0; i
< models_cached
; i
++) {
3025 if(stricmp(name
, model_cache
[i
].name
) == 0)
3026 return model_cache
[i
].path
;
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
3039 s_model
*newchar
= calloc(1, sizeof(s_model
));
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
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;
3093 newchar
->mprate
= 1;
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;
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;
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);
3173 if((tempmodel
= findmodel(value
)) && tempmodel
!= newchar
)
3174 shutdown(1, "Duplicate model name '%s'", value
);
3175 /*if((tempmodel=find_model(value))) {
3178 model_cache
[cacheindex
].model
= newchar
;
3179 newchar
->name
= model_cache
[cacheindex
].name
;
3180 if(stricmp(newchar
->name
, "steam") == 0) {
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);
3203 lc(value
, GET_ARGP_LEN(1));
3204 stringswitch_d(lcm_cmdtype
, value
) {
3205 stringcase(lcm_cmdtype
, none
):
3206 newchar
->type
= TYPE_NONE
;
3208 stringcase(lcm_cmdtype
, player
):
3209 newchar
->type
= TYPE_PLAYER
;
3210 newchar
->nopassiveblock
= 1;
3211 for(i
= 0; i
< MAX_ATCHAIN
; i
++) {
3213 newchar
->atchain
[i
] = 1;
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;
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;
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;
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;
3262 stringcase(lcm_cmdtype
, steamer
):
3263 newchar
->type
= TYPE_STEAMER
;
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;
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;
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;
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;
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;
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;
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);
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;
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;
3386 stringcase(lcm_cmdsubtype
, notgrab
):
3387 newchar
->subtype
= SUBTYPE_NOTGRAB
; // 7-1-2005 notgrab type
3389 stringcase(lcm_cmdsubtype
, touch
):
3390 newchar
->subtype
= SUBTYPE_TOUCH
; // 7-1-2005 notgrab type
3392 stringcase(lcm_cmdsubtype
, weapon
):
3393 newchar
->subtype
= SUBTYPE_WEAPON
; // 7-1-2005 notgrab type
3395 stringcase(lcm_cmdsubtype
, noskip
):
3396 // Text animation cannot be skipped if subtype noskip
3397 newchar
->subtype
= SUBTYPE_NOSKIP
;
3399 stringcase(lcm_cmdsubtype
, flydie
):
3400 // Obstacle will fly across the screen when hit if subtype flydie
3401 newchar
->subtype
= SUBTYPE_FLYDIE
;
3403 stringcase(lcm_cmdsubtype
, both
):
3404 newchar
->subtype
= SUBTYPE_BOTH
;
3406 stringcase(lcm_cmdsubtype
, project
):
3407 newchar
->subtype
= SUBTYPE_PROJECTILE
;
3409 stringcase(lcm_cmdsubtype
, follow
):
3410 newchar
->subtype
= SUBTYPE_FOLLOW
;
3412 stringcase(lcm_cmdsubtype
, chase
):
3413 newchar
->subtype
= SUBTYPE_CHASE
;
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
;
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;
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
;
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
) {
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
;
3475 stringcase(lcm_cmdhostile
, player
):
3476 newchar
->hostile
|= TYPE_PLAYER
;
3478 stringcase(lcm_cmdhostile
, obstacle
):
3479 newchar
->hostile
|= TYPE_OBSTACLE
;
3481 stringcase(lcm_cmdhostile
, shot
):
3482 newchar
->hostile
|= TYPE_SHOT
;
3484 stringcase(lcm_cmdhostile
, npc
):
3485 newchar
->hostile
|= TYPE_NPC
;
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
) {
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
;
3516 stringcase(lcm_cmdcandamage
, player
):
3517 newchar
->candamage
|= TYPE_PLAYER
;
3519 stringcase(lcm_cmdcandamage
, obstacle
):
3520 newchar
->candamage
|= TYPE_OBSTACLE
;
3522 stringcase(lcm_cmdcandamage
, shot
):
3523 newchar
->candamage
|= TYPE_SHOT
;
3525 stringcase(lcm_cmdcandamage
, npc
):
3526 newchar
->candamage
|= TYPE_NPC
;
3528 stringcase(lcm_cmdcandamage
, ground
):
3529 newchar
->ground
= 1;
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
) {
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
;
3557 stringcase(lcm_cmdprojectilehit
, player
):
3558 newchar
->projectilehit
|= TYPE_PLAYER
;
3560 stringcase(lcm_cmdprojectilehit
, obstacle
):
3561 newchar
->projectilehit
|= TYPE_OBSTACLE
;
3563 stringcase(lcm_cmdprojectilehit
, shot
):
3564 newchar
->projectilehit
|= TYPE_SHOT
;
3566 stringcase(lcm_cmdprojectilehit
, npc
):
3567 newchar
->projectilehit
|= TYPE_NPC
;
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));
3596 newchar
->aimove
= 0;
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
;
3605 stringcase(lcm_cmdaimove
, chase
):
3606 newchar
->aimove
|= AIMOVE1_CHASE
;
3608 stringcase(lcm_cmdaimove
, chasex
):
3609 newchar
->aimove
|= AIMOVE1_CHASEX
;
3611 stringcase(lcm_cmdaimove
, chasez
):
3612 newchar
->aimove
|= AIMOVE1_CHASEZ
;
3614 stringcase(lcm_cmdaimove
, avoid
):
3615 newchar
->aimove
|= AIMOVE1_AVOID
;
3617 stringcase(lcm_cmdaimove
, avoidx
):
3618 newchar
->aimove
|= AIMOVE1_AVOIDX
;
3620 stringcase(lcm_cmdaimove
, avoidz
):
3621 newchar
->aimove
|= AIMOVE1_AVOIDZ
;
3623 stringcase(lcm_cmdaimove
, wander
):
3624 newchar
->aimove
|= AIMOVE1_WANDER
;
3626 stringcase(lcm_cmdaimove
, biker
):
3627 newchar
->aimove
|= AIMOVE1_BIKER
;
3629 stringcase(lcm_cmdaimove
, arrow
):
3630 newchar
->aimove
|= AIMOVE1_ARROW
;
3631 if(!newchar
->offscreenkill
)
3632 newchar
->offscreenkill
= 200;
3634 stringcase(lcm_cmdaimove
, star
):
3635 newchar
->aimove
|= AIMOVE1_STAR
;
3637 stringcase(lcm_cmdaimove
, bomb
):
3638 newchar
->aimove
|= AIMOVE1_BOMB
;
3640 stringcase(lcm_cmdaimove
, nomove
):
3641 newchar
->aimove
|= AIMOVE1_NOMOVE
;
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
;
3655 shutdown(1, "Model '%s' has invalid A.I. move switch: '%s'", filename
, value
);
3659 void lcmHandleCommandWeapons(ArgList
* arglist
, s_model
* newchar
) {
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);
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;
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
);
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)
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
;
3716 stringcase(lcm_cmdcom
, d
):
3717 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_MOVEDOWN
;
3719 stringcase(lcm_cmdcom
, f
):
3720 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_FORWARD
;
3722 stringcase(lcm_cmdcom
, b
):
3723 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_BACKWARD
;
3725 stringcase(lcm_cmdcom
, a
):
3726 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_ATTACK
;
3728 stringcase(lcm_cmdcom
, a2
):
3729 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_ATTACK2
;
3731 stringcase(lcm_cmdcom
, a3
):
3732 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_ATTACK3
;
3734 stringcase(lcm_cmdcom
, a4
):
3735 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_ATTACK4
;
3737 stringcase(lcm_cmdcom
, j
):
3738 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_JUMP
;
3740 stringcase(lcm_cmdcom
, s
):
3741 stringcase(lcm_cmdcom
, k
):
3742 newchar
->special
[newchar
->specials_loaded
][i
] = FLAG_SPECIAL
;
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
3756 for(i
= 0, t
= 1; i
< MAX_SPECIAL_INPUTS
- 3; i
++, t
++) {
3757 *value
= GET_ARGP(t
);
3760 lc(*value
, GET_ARGP_LEN(t
));
3761 if(!switchComAndCancelCommon(value
, newchar
, i
)) {
3762 if((!strnicmp(*value
, "freespecial", 11)) &&
3765 ((*value
)[11] >= '1' && (*value
)[11] <= '9')
3768 tempInt
= atoi((*value
) + 11);
3771 newchar
->special
[newchar
->specials_loaded
]
3772 [MAX_SPECIAL_INPUTS
- 2] = dyn_anims
.animspecials
[tempInt
- 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.
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
3791 newanim
->cancel
= 3;
3792 for(i
= 0, t
= 4; i
< MAX_SPECIAL_INPUTS
- 6; i
++, t
++) {
3793 *value
= GET_ARGP(t
);
3796 lc((*value
), GET_ARGP_LEN(t
));
3797 if(!switchComAndCancelCommon(value
, newchar
, i
)) {
3798 if(strnicmp((*value
), "freespecial", 11) == 0
3800 || ((*value
)[11] >= '1' && (*value
)[11] <= '9'))) {
3801 tempInt
= atoi((*value
) + 11);
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
3810 *shutdownmessage
= "Invalid cancel command!";
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
) {
3820 "Too many Freespecials and/or Cancels. Please increase Maxfreespecials";
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') {
3934 if(endsWithNumber
) {
3935 commandIndex
= atoi(lowercase_buf
+ l
);
3936 lowercase_buf
[l
] = 0;
3937 if(commandIndex
< 1)
3941 // Create new animation
3942 (*newanim
) = alloc_anim();
3944 *shutdownmessage
= (char*) E_OUT_OF_MEMORY
;
3947 (*newanim
)->model_index
= newchar
->index
;
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
),
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
),
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
),
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
),
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
4089 stringcase(lcm_cmdanim
, burn
):
4090 stringcase(lcm_cmdanim
, shock
):
4091 (*newanim
)->bounce
= 4;
4093 stringcase(lcm_cmdanim
, upper
):
4094 attack
->counterattack
= 100; //default to 100
4095 (*newanim
)->range
[0] = -10;
4096 (*newanim
)->range
[1] = 120;
4098 stringcase(lcm_cmdanim
, block
):
4099 // Now enemies can block attacks on occasion
4100 (*newanim
)->range
[0] = 1;
4101 (*newanim
)->range
[1] = 100;
4103 stringcase(lcm_cmdanim
, attack
):
4104 (*ani_id
) = dyn_anims
.animattacks
[commandIndex
- 1];
4106 stringcase(lcm_cmdanim
, walk
):
4107 (*ani_id
) = dyn_anims
.animwalks
[commandIndex
- 1];
4109 stringcase(lcm_cmdanim
, up
):
4110 (*ani_id
) = dyn_anims
.animups
[commandIndex
- 1];
4112 stringcase(lcm_cmdanim
, down
):
4113 (*ani_id
) = dyn_anims
.animdowns
[commandIndex
- 1];
4115 stringcase(lcm_cmdanim
, backwalk
):
4116 (*ani_id
) = dyn_anims
.animbackwalks
[commandIndex
- 1];
4118 stringcase(lcm_cmdanim
, idle
):
4119 (*ani_id
) = dyn_anims
.animidles
[commandIndex
- 1];
4121 stringcase(lcm_cmdanim
, follow
):
4122 (*ani_id
) = dyn_anims
.animfollows
[commandIndex
- 1];
4124 stringcase(lcm_cmdanim
, jumpattack
):
4125 if(commandIndex
== 1 && newchar
->jumpheight
== 4) {
4126 (*newanim
)->range
[0] = 150;
4127 (*newanim
)->range
[1] = 200;
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;
4138 stringcase(lcm_cmdanim
, special
):
4139 if(commandIndex
== 1) (*newanim
)->energycost
[0] = 6;
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];
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];
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];
4162 stringcase(lcm_cmdanim
, fall
):
4163 if(commandIndex
== 1)
4164 (*newanim
)->bounce
= 4;
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];
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];
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];
4186 stringcase(lcm_cmdanim
, freespecial
):
4191 (*ani_id
) = dyn_anims
.animspecials
[commandIndex
- 1];
4192 switch (commandIndex
) {
4194 flag
= FLAG_FORWARD
;
4195 ani
= ANI_FREESPECIAL
;
4198 flag
= FLAG_MOVEDOWN
;
4199 ani
= ANI_FREESPECIAL2
;
4203 ani
= ANI_FREESPECIAL3
;
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
++;
4221 newchar
->animation
[(*ani_id
)] = (*newanim
);
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] = "";
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;
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] = {
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
4268 "{\n" " int frame = getlocalvar(\"frame\");\n" " int animnum = getlocalvar(\"animnum\");\n" "\n}\n";
4270 static const char *sur_text
= // end of function text
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
4285 static const char *comma_text
= // arguments separator
4288 static const char *call_text
= //begin of function call
4291 static const char *endcall_text
= //end of function call
4295 modelAttackCommands atk_cmd
;
4296 s_scripts tempscripts
;
4300 printf("load_cached_model: %s, unload: %d\n", name
, unload
);
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
);
4309 cacheindex
= get_cached_model_index(name
);
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);
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!
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
4344 //command = GET_ARG(0);
4345 if(ParseArgs(&arglist
, buf
+ pos
, argbuf
)) {
4346 command
= GET_ARG(0);
4347 cmd
= getModelCommand(modelcmdlist
, command
);
4350 case CMD_MODEL_SUBCLASS
:
4351 //inherit everything from an existing, cached model
4352 tempmodel
= findmodel(GET_ARG(1));
4355 "tried to subclass a non-existing/not previously loaded model!";
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
;
4365 case CMD_MODEL_NAME
:
4366 lcmHandleCommandName(&arglist
, newchar
, cacheindex
);
4368 case CMD_MODEL_TYPE
:
4369 lcmHandleCommandType(&arglist
, newchar
, filename
);
4371 case CMD_MODEL_SUBTYPE
:
4372 lcmHandleCommandSubtype(&arglist
, newchar
, filename
);
4374 case CMD_MODEL_STATS
:
4376 newchar
->stats
[atoi(value
)] = GET_FLOAT_ARG(2);
4378 case CMD_MODEL_SCROLL
:
4380 newchar
->scroll
= atof(value
);
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
;
4385 newchar
->makeinv
= -newchar
->makeinv
;
4387 case CMD_MODEL_RISEINV
:
4388 newchar
->riseinv
= GET_INT_ARG(1) * GAME_SPEED
;
4390 newchar
->riseinv
= -newchar
->riseinv
;
4392 case CMD_MODEL_LOAD
:
4394 tempmodel
= findmodel(value
);
4396 load_cached_model(value
, name
, GET_INT_ARG(2));
4398 update_model_loadflag(tempmodel
, GET_INT_ARG(2));
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
4404 case CMD_MODEL_SMARTBOMB
:
4405 lcmHandleCommandSmartbomb(&arglist
, newchar
, filename
);
4407 case CMD_MODEL_HITENEMY
: // Flag to determine if an enemy projectile will hit enemies
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
4415 case CMD_MODEL_HOSTILE
:
4416 lcmHandleCommandHostile(&arglist
, newchar
);
4418 case CMD_MODEL_CANDAMAGE
:
4419 lcmHandleCommandCandamage(&arglist
, newchar
);
4421 case CMD_MODEL_PROJECTILEHIT
:
4422 lcmHandleCommandProjectilehit(&arglist
, newchar
);
4424 case CMD_MODEL_AIMOVE
:
4425 lcmHandleCommandAimove(&arglist
, newchar
, &aimoveset
, filename
);
4427 case CMD_MODEL_AIATTACK
:
4428 if(newchar
->aiattack
== -1)
4429 newchar
->aiattack
= 0;
4430 //do nothing for now, until ai attack is implemented
4432 case CMD_MODEL_SUBJECT_TO_WALL
:
4433 newchar
->subject_to_wall
= (0 != GET_INT_ARG(1));
4435 case CMD_MODEL_SUBJECT_TO_HOLE
:
4436 newchar
->subject_to_hole
= (0 != GET_INT_ARG(1));
4438 case CMD_MODEL_SUBJECT_TO_PLATFORM
:
4439 newchar
->subject_to_platform
= (0 != GET_INT_ARG(1));
4441 case CMD_MODEL_SUBJECT_TO_OBSTACLE
:
4442 newchar
->subject_to_obstacle
= (0 != GET_INT_ARG(1));
4444 case CMD_MODEL_SUBJECT_TO_GRAVITY
:
4445 newchar
->subject_to_gravity
= (0 != GET_INT_ARG(1));
4447 case CMD_MODEL_SUBJECT_TO_SCREEN
:
4448 newchar
->subject_to_screen
= (0 != GET_INT_ARG(1));
4450 case CMD_MODEL_SUBJECT_TO_MINZ
:
4451 newchar
->subject_to_minz
= (0 != GET_INT_ARG(1));
4453 case CMD_MODEL_SUBJECT_TO_MAXZ
:
4454 newchar
->subject_to_maxz
= (0 != GET_INT_ARG(1));
4456 case CMD_MODEL_NO_ADJUST_BASE
:
4457 newchar
->no_adjust_base
= (0 != GET_INT_ARG(1));
4460 case CMD_MODEL_WEAPLOSS
:
4461 newchar
->weaploss
[0] = GET_INT_ARG(1);
4462 newchar
->weaploss
[1] = GET_INT_ARG(2);
4464 case CMD_MODEL_WEAPONS
:
4465 lcmHandleCommandWeapons(&arglist
, newchar
);
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
:
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);
4522 case CMD_MODEL_SHOOTNUM
: //here weapons things like shoot rest type of weapon ect..by tails
4523 newchar
->shootnum
= GET_INT_ARG(1);
4525 case CMD_MODEL_RELOAD
:
4526 newchar
->reload
= GET_INT_ARG(1);
4530 case CMD_MODEL_THOLD
:
4531 // Threshold for enemies/players block
4532 newchar
->thold
= GET_INT_ARG(1);
4534 case CMD_MODEL_THROWFRAMEWAIT
:
4535 newchar
->throwframewait
= GET_INT_ARG(1);
4537 case CMD_MODEL_BLOCKODDS
:
4538 // Odds that an attack will hit an enemy (1 : blockodds)
4539 newchar
->blockodds
= GET_INT_ARG(1);
4541 case CMD_MODEL_THROWDAMAGE
:
4542 newchar
->throwdamage
= GET_INT_ARG(1);
4544 case CMD_MODEL_HEIGHT
:
4545 newchar
->height
= GET_INT_ARG(1);
4547 case CMD_MODEL_COUNTER
:
4548 newchar
->counter
= GET_INT_ARG(1);
4554 case CMD_MODEL_NOATFLASH
: // Flag to determine if an opponents attack spawns their flash or not
4555 newchar
->noatflash
= GET_INT_ARG(1);
4557 case CMD_MODEL_SETLAYER
:
4558 newchar
->setlayer
= GET_INT_ARG(1);
4560 case CMD_MODEL_GRABFORCE
:
4561 newchar
->grabforce
= GET_INT_ARG(1);
4563 case CMD_MODEL_HEALTH
:
4564 newchar
->health
= GET_INT_ARG(1);
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);
4572 case CMD_MODEL_OFFSCREENKILL
:
4573 newchar
->offscreenkill
= GET_INT_ARG(1);
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
:
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
);
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]);
4615 case CMD_MODEL_BRANCH
: // for endlevel item's level branch
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
);
4623 case CMD_MODEL_CANTGRAB
:
4624 case CMD_MODEL_NOTGRAB
:
4625 tempInt
= GET_INT_ARG(1);
4627 newchar
->grabforce
= -999999;
4629 newchar
->antigrab
= 1;
4631 case CMD_MODEL_SPEED
:
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;
4640 case CMD_MODEL_SPEEDF
:
4642 newchar
->speed
= atof(value
);
4644 case CMD_MODEL_JUMPSPEED
:
4646 newchar
->jumpspeed
= atof(value
);
4647 newchar
->jumpspeed
/= 10;
4649 case CMD_MODEL_JUMPSPEEDF
:
4650 newchar
->jumpspeed
= GET_FLOAT_ARG(1);
4652 case CMD_MODEL_ANTIGRAVITY
:
4653 newchar
->antigravity
= GET_FLOAT_ARG(1) / 100.f
;
4655 case CMD_MODEL_STEALTH
:
4656 newchar
->stealth
[0] = GET_INT_ARG(1);
4657 newchar
->stealth
[1] = GET_INT_ARG(2);
4659 case CMD_MODEL_JUGGLEPOINTS
:
4660 newchar
->jugglepoints
[0] = GET_INT_ARG(1);
4661 newchar
->jugglepoints
[1] = GET_INT_ARG(1);
4663 case CMD_MODEL_GUARDPOINTS
:
4664 newchar
->guardpoints
[0] = GET_INT_ARG(1);
4665 newchar
->guardpoints
[1] = GET_INT_ARG(1);
4667 case CMD_MODEL_DEFENSE
:
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);
4703 case CMD_MODEL_OFFENSE
:
4706 atk_cmd
= getModelAttackCommand(modelsattackcmdlist
, value
);
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
;
4721 case CMD_MODEL_JUMPHEIGHT
:
4722 newchar
->jumpheight
= GET_FLOAT_ARG(1);
4724 case CMD_MODEL_JUMPMOVE
:
4725 newchar
->jumpmovex
= GET_INT_ARG(1);
4726 newchar
->jumpmovez
= GET_INT_ARG(2);
4728 case CMD_MODEL_KNOCKDOWNCOUNT
:
4729 newchar
->knockdowncount
= GET_FLOAT_ARG(1);
4731 case CMD_MODEL_GRABDISTANCE
:
4732 newchar
->grabdistance
= GET_FLOAT_ARG(1); // 30-12-2004 and store for character
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.
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.
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
4747 newchar
->nodrop
= 1;
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);
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);
4765 case CMD_MODEL_THROW
:
4766 newchar
->throwdist
= GET_FLOAT_ARG(1);
4767 newchar
->throwheight
= GET_FLOAT_ARG(2);
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;
4774 case CMD_MODEL_DIESOUND
:
4775 newchar
->diesound
= sound_load_sample(GET_ARG(1), packfile
, 0);
4777 case CMD_MODEL_ICON
:
4778 if(newchar
->icon
> -1) {
4779 shutdownmessage
= "model has multiple icons defined";
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
;
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
:
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
);
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);
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);
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);
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);
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;
4843 case CMD_MODEL_COMBOSTYLE
:
4844 newchar
->combostyle
= GET_INT_ARG(1);
4846 case CMD_MODEL_CREDIT
:
4847 newchar
->credit
= GET_INT_ARG(1);
4849 case CMD_MODEL_NOPAIN
:
4850 newchar
->nopain
= GET_INT_ARG(1);
4852 case CMD_MODEL_ESCAPEHITS
:
4853 // How many times an enemy can be hit before retaliating
4854 newchar
->escapehits
= GET_INT_ARG(1);
4856 case CMD_MODEL_CHARGERATE
:
4857 // How much mp does this character gain while recharging?
4858 newchar
->chargerate
= GET_INT_ARG(1);
4860 case CMD_MODEL_MPRATE
:
4861 newchar
->mprate
= GET_INT_ARG(1);
4863 case CMD_MODEL_MPSET
:
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.
4872 case CMD_MODEL_SLEEPWAIT
:
4873 newchar
->sleepwait
= GET_INT_ARG(1);
4875 case CMD_MODEL_GUARDRATE
:
4876 newchar
->guardrate
= GET_INT_ARG(1);
4878 case CMD_MODEL_AGGRESSION
:
4879 newchar
->aggression
= GET_INT_ARG(1);
4881 case CMD_MODEL_RISETIME
:
4882 newchar
->risetime
[0] = GET_INT_ARG(1);
4883 newchar
->risetime
[1] = GET_INT_ARG(2);
4885 case CMD_MODEL_FACING
:
4886 newchar
->facing
= GET_INT_ARG(1);
4888 case CMD_MODEL_TURNDELAY
:
4889 newchar
->turndelay
= GET_INT_ARG(1);
4891 case CMD_MODEL_LIFESPAN
:
4892 newchar
->lifespan
= GET_FLOAT_ARG(1) * GAME_SPEED
;
4894 case CMD_MODEL_SUMMONKILL
:
4895 newchar
->summonkill
= GET_INT_ARG(1);
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
);
4903 case CMD_MODEL_LIFEBARSTATUS
:
4904 _readbarstatus(buf
+ pos
, &(newchar
->hpbarstatus
));
4905 newchar
->hpbarstatus
.colourtable
= &color_tables
.hp
;
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
);
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
);
4920 tempInt
= lcmHandleCommandCom(&arglist
, newchar
, &value
, &shutdownmessage
);
4922 else if(tempInt
== 1) {
4923 printf("WARNING: Invalid freespecial command '%s' in '%s', line %u\n", value
, filename
, line
);
4927 // End section for custom freespecials
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
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;
4942 case 0: // uhm wait, we just tested for !errorVal...
4944 "Failed to create colourmap. Image Used Twice!";
4949 "Failed to create colourmap. MAX_COLOUR_MAPS full!";
4954 "Failed to create colourmap. Failed to tracemalloc(256)!";
4959 "Failed to create colourmap. Failed to create bitmap1";
4964 "Failed to create colourmap. Failed to create bitmap2";
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",
4976 else if(newchar
->palette
== NULL
) {
4978 newchar
->palette
= malloc(PAL_BYTES
);
4979 if(loadimagepalette(value
, packfile
, newchar
->palette
) == 0) {
4980 shutdownmessage
= "Failed to load palette!";
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",
4990 else if(newchar
->maps_loaded
< MAX_COLOUR_MAPS
) {
4992 newchar
->colourmap
[(int) newchar
->maps_loaded
] = malloc(PAL_BYTES
);
4995 newchar
->colourmap
[(int) newchar
->maps_loaded
]) == 0) {
4996 shutdownmessage
= "Failed to load palette!";
4999 newchar
->maps_loaded
++;
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",
5008 newchar
->globalmap
= GET_INT_ARG(1);
5010 case CMD_MODEL_ALPHA
:
5011 newchar
->alpha
= GET_INT_ARG(1);
5013 case CMD_MODEL_REMOVE
:
5014 newchar
->remove
= GET_INT_ARG(1);
5016 case CMD_MODEL_SCRIPT
:
5017 //load the update script
5018 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.update_script
,
5019 "updateentityscript", filename
);
5021 case CMD_MODEL_THINKSCRIPT
:
5022 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.think_script
, "thinkscript",
5025 case CMD_MODEL_TAKEDAMAGESCRIPT
:
5026 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.takedamage_script
,
5027 "takedamagescript", filename
);
5029 case CMD_MODEL_ONFALLSCRIPT
:
5030 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onfall_script
,
5031 "onfallscript", filename
);
5033 case CMD_MODEL_ONPAINSCRIPT
:
5034 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onpain_script
,
5035 "onpainscript", filename
);
5037 case CMD_MODEL_ONBLOCKSSCRIPT
:
5038 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onblocks_script
,
5039 "onblocksscript", filename
);
5041 case CMD_MODEL_ONBLOCKWSCRIPT
:
5042 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onblockw_script
,
5043 "onblockwscript", filename
);
5045 case CMD_MODEL_ONBLOCKOSCRIPT
:
5046 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onblocko_script
,
5047 "onblockoscript", filename
);
5049 case CMD_MODEL_ONBLOCKZSCRIPT
:
5050 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onblockz_script
,
5051 "onblockzscript", filename
);
5053 case CMD_MODEL_ONBLOCKASCRIPT
:
5054 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onblocka_script
,
5055 "onblockascript", filename
);
5057 case CMD_MODEL_ONMOVEXSCRIPT
:
5058 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onmovex_script
,
5059 "onmovexscript", filename
);
5061 case CMD_MODEL_ONMOVEZSCRIPT
:
5062 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onmovez_script
,
5063 "onmovezscript", filename
);
5065 case CMD_MODEL_ONMOVEASCRIPT
:
5066 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onmovea_script
,
5067 "onmoveascript", filename
);
5069 case CMD_MODEL_ONDEATHSCRIPT
:
5070 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.ondeath_script
,
5071 "ondeathscript", filename
);
5073 case CMD_MODEL_ONKILLSCRIPT
:
5074 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onkill_script
,
5075 "onkillscript", filename
);
5077 case CMD_MODEL_DIDBLOCKSCRIPT
:
5078 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.didblock_script
,
5079 "didblockscript", filename
);
5081 case CMD_MODEL_ONDOATTACKSCRIPT
:
5082 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.ondoattack_script
,
5083 "ondoattackscript", filename
);
5085 case CMD_MODEL_DIDHITSCRIPT
:
5086 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.didhit_script
,
5087 "didhitscript", filename
);
5089 case CMD_MODEL_ONSPAWNSCRIPT
:
5090 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.onspawn_script
,
5091 "onspawnscript", filename
);
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!";
5099 //dont compile, until at end of this function
5101 case CMD_MODEL_KEYSCRIPT
:
5102 lcmHandleCommandScripts(&arglist
, newchar
->scripts
.key_script
,
5103 "entitykeyscript", filename
);
5105 case CMD_MODEL_ANIM
:
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
));
5117 attack
= emptyattack
;
5118 attack
.hitsound
= -1;
5119 attack
.hitflash
= -1;
5120 attack
.blockflash
= -1;
5121 attack
.blocksound
= -1;
5122 drawmethod
= plainmethod
;
5130 tempInt
= lcmHandleCommandAnim(&arglist
, newchar
, &newanim
, &ani_id
, &value
, &shutdownmessage
, &attack
);
5132 else if (tempInt
== 1) {
5133 printf("WARNING: invalid animation name '%s', file '%s', line %u\n", value
, filename
, line
);
5139 case CMD_MODEL_LOOP
:
5141 shutdownmessage
= "Can't set loop: no animation specified!";
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.
5148 case CMD_MODEL_ANIMHEIGHT
:
5149 newanim
->height
= GET_INT_ARG(1);
5151 case CMD_MODEL_DELAY
:
5152 delay
= GET_INT_ARG(1);
5154 case CMD_MODEL_OFFSET
:
5155 offset
[0] = GET_INT_ARG(1);
5156 offset
[1] = GET_INT_ARG(2);
5158 case CMD_MODEL_SHADOWCOORDS
:
5159 shadow_xz
[0] = GET_INT_ARG(1);
5160 shadow_xz
[1] = GET_INT_ARG(2);
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);
5169 case CMD_MODEL_MPONLY
:
5170 newanim
->energycost
[1] = GET_INT_ARG(1);
5172 case CMD_MODEL_CHARGETIME
:
5173 newanim
->chargetime
= GET_FLOAT_ARG(1);
5175 case CMD_MODEL_DIVE
: //dive kicks
5176 newanim
->dive
[0] = GET_FLOAT_ARG(1);
5177 newanim
->dive
[1] = GET_FLOAT_ARG(2);
5179 case CMD_MODEL_DIVE1
:
5180 newanim
->dive
[0] = GET_FLOAT_ARG(1);
5182 case CMD_MODEL_DIVE2
:
5183 newanim
->dive
[1] = GET_FLOAT_ARG(1);
5185 case CMD_MODEL_ATTACKONE
:
5186 newanim
->attackone
= GET_INT_ARG(1);
5188 case CMD_MODEL_COUNTERATTACK
:
5189 attack
.counterattack
= GET_INT_ARG(1);
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;
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;
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;
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));
5220 case CMD_MODEL_CUSTPSHOTNO
:
5221 newanim
->custpshotno
= get_cached_model_index(GET_ARG(1));
5223 case CMD_MODEL_CUSTBOMB
:
5224 case CMD_MODEL_CUSTPBOMB
:
5225 newanim
->custbomb
= get_cached_model_index(GET_ARG(1));
5227 case CMD_MODEL_CUSTSTAR
:
5228 newanim
->custstar
= get_cached_model_index(GET_ARG(1));
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
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;
5246 newanim
->jumpv
= newchar
->jumpheight
;
5247 newanim
->jumpz
= newanim
->jumpx
= 0;
5250 if(newchar
->type
!= TYPE_ENEMY
5251 && newchar
->type
!= TYPE_NPC
)
5252 newanim
->jumpz
= newanim
->jumpx
= 0;
5255 newanim
->jumpx
= (float) 1.3;
5262 newanim
->jumpd
= get_cached_model_index(value
);
5264 newanim
->jumpd
= -1;
5268 case CMD_MODEL_BOUNCEFACTOR
:
5269 newanim
->bounce
= GET_FLOAT_ARG(1);
5271 case CMD_MODEL_LANDFRAME
:
5272 newanim
->landframe
[0] = GET_INT_ARG(1);
5275 newanim
->landframe
[1] = get_cached_model_index(value
);
5277 newanim
->landframe
[1] = -1;
5279 case CMD_MODEL_DROPFRAME
:
5280 newanim
->dropframe
= GET_INT_ARG(1);
5282 case CMD_MODEL_CANCEL
:
5283 if(lcmHandleCommandCancel(&arglist
, newchar
, newanim
, &value
, ani_id
, &shutdownmessage
, filename
, command
) == -1) goto lCleanup
;
5285 case CMD_MODEL_SOUND
:
5286 soundtoplay
= sound_load_sample(GET_ARG(1), packfile
, 0);
5288 case CMD_MODEL_HITFX
:
5289 attack
.hitsound
= sound_load_sample(GET_ARG(1), packfile
, 0);
5291 case CMD_MODEL_BLOCKFX
:
5292 attack
.blocksound
= sound_load_sample(GET_ARG(1), packfile
, 0);
5294 case CMD_MODEL_FASTATTACK
:
5295 newanim
->fastattack
= GET_INT_ARG(1);
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);
5304 case CMD_MODEL_BBOXZ
:
5305 bbox
[4] = GET_INT_ARG(1);
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
++) ;
5311 for(i
= 0; i
< 6; i
++)
5312 platform
[i
+ 2] = GET_FLOAT_ARG(i
+ 1);
5313 platform
[0] = 99999;
5315 for(i
= 0; i
< 8; i
++)
5316 platform
[i
] = GET_FLOAT_ARG(i
+ 1);
5318 case CMD_MODEL_DRAWMETHOD
:
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;
5349 case CMD_MODEL_NODRAWMETHOD
:
5350 //disable special effects
5351 drawmethod
.flag
= 0;
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
5396 case CMD_MODEL_ATTACK
:
5397 case CMD_MODEL_ATTACK1
:
5398 attack
.attack_type
= ATK_NORMAL
;
5400 case CMD_MODEL_ATTACK2
:
5401 attack
.attack_type
= ATK_NORMAL2
;
5403 case CMD_MODEL_ATTACK3
:
5404 attack
.attack_type
= ATK_NORMAL3
;
5406 case CMD_MODEL_ATTACK4
:
5407 attack
.attack_type
= ATK_NORMAL4
;
5409 case CMD_MODEL_ATTACK5
:
5410 attack
.attack_type
= ATK_NORMAL5
;
5412 case CMD_MODEL_ATTACK6
:
5413 attack
.attack_type
= ATK_NORMAL6
;
5415 case CMD_MODEL_ATTACK7
:
5416 attack
.attack_type
= ATK_NORMAL7
;
5418 case CMD_MODEL_ATTACK8
:
5419 attack
.attack_type
= ATK_NORMAL8
;
5421 case CMD_MODEL_ATTACK9
:
5422 attack
.attack_type
= ATK_NORMAL9
;
5424 case CMD_MODEL_ATTACK10
:
5425 attack
.attack_type
= ATK_NORMAL10
;
5427 case CMD_MODEL_SHOCK
:
5428 attack
.attack_type
= ATK_SHOCK
;
5430 case CMD_MODEL_BURN
:
5431 attack
.attack_type
= ATK_BURN
;
5433 case CMD_MODEL_STEAL
:
5435 attack
.attack_type
= ATK_STEAL
;
5437 case CMD_MODEL_FREEZE
:
5438 attack
.attack_type
= ATK_FREEZE
;
5440 attack
.freezetime
= GET_INT_ARG(6) * GAME_SPEED
;
5441 attack
.forcemap
= -1;
5442 attack
.attack_drop
= 0;
5444 case CMD_MODEL_ITEMBOX
:
5445 attack
.attack_type
= ATK_ITEM
;
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;
5454 case CMD_MODEL_ATTACKZ
:
5455 case CMD_MODEL_HITZ
:
5456 attack
.attack_coords
[4] = GET_INT_ARG(1);
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
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
5483 // Over The Ground hit.
5484 attack
.otg
= GET_INT_ARG(1);
5486 case CMD_MODEL_JUGGLECOST
:
5487 // if cost >= opponents jugglepoints , we can juggle
5488 attack
.jugglecost
= GET_INT_ARG(1);
5490 case CMD_MODEL_GUARDCOST
:
5491 // if cost >= opponents guardpoints , opponent will play guardcrush anim
5492 attack
.guardcost
= GET_INT_ARG(1);
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;
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);
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);
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);
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);
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);
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.
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.
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
;
5549 case CMD_MODEL_IDLE
:
5550 idle
= GET_INT_ARG(1);
5552 case CMD_MODEL_MOVE
:
5553 move
= GET_INT_ARG(1);
5555 case CMD_MODEL_MOVEZ
:
5556 movez
= GET_INT_ARG(1);
5558 case CMD_MODEL_MOVEA
:
5559 movea
= GET_INT_ARG(1);
5561 case CMD_MODEL_SETA
:
5562 seta
= GET_INT_ARG(1);
5564 case CMD_MODEL_FSHADOW
:
5565 frameshadow
= GET_INT_ARG(1);
5567 case CMD_MODEL_RANGE
:
5569 shutdownmessage
= "Cannot set range: no animation!";
5572 newanim
->range
[0] = GET_INT_ARG(1);
5573 newanim
->range
[1] = GET_INT_ARG(2);
5575 case CMD_MODEL_RANGEZ
:
5577 shutdownmessage
= "Cannot set rangez: no animation!";
5580 newanim
->range
[2] = GET_INT_ARG(1);
5581 newanim
->range
[3] = GET_INT_ARG(2);
5583 case CMD_MODEL_RANGEA
:
5585 shutdownmessage
= "Cannot set rangea: no animation!";
5588 newanim
->range
[4] = GET_INT_ARG(1);
5589 newanim
->range
[5] = GET_INT_ARG(2);
5591 case CMD_MODEL_RANGEB
:
5593 shutdownmessage
= "Cannot set rangeb: no animation!";
5596 newanim
->range
[6] = GET_INT_ARG(1);
5597 newanim
->range
[7] = GET_INT_ARG(2);
5599 case CMD_MODEL_FRAME
:
5602 shutdownmessage
= "Cannot add frame: animation not specified!";
5606 if(frameset
&& framecount
>= 0)
5607 framecount
= -framecount
;
5609 value3
= findarg(buf
+ pos
+ peek
, 0);
5610 if(stricmp(value3
, "frame") == 0)
5612 if((stricmp(value3
, "anim") == 0) || (pos
+ peek
>= size
))
5615 while(buf
[pos
+ peek
] && buf
[pos
+ peek
] != '\n'
5616 && buf
[pos
+ peek
] != '\r')
5618 while(buf
[pos
+ peek
] == '\n' || buf
[pos
+ peek
] == '\r')
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
)
5631 shutdownmessage
= "Failed to load palette!";
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
;
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];
5677 shadow_coords
[0] = shadow_xz
[0] - offset
[0];
5678 shadow_coords
[1] = shadow_xz
[1] - offset
[1];
5680 shadow_coords
[0] = shadow_coords
[1] = 0;
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
));
5692 case CMD_MODEL_ALPHAMASK
:
5694 shutdownmessage
= "Cannot add alpha mask: animation not specified!";
5697 if(maskindex
>= 0) {
5699 "Cannot add alpha mask: a mask has already been specified for this frame!";
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
5708 case CMD_MODEL_FLIPFRAME
:
5709 newanim
->flipframe
= GET_INT_ARG(1);
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;
5718 case CMD_MODEL_FOLLOWCOND
:
5719 newanim
->followcond
= GET_INT_ARG(1);
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);
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);
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);
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;
5745 case CMD_MODEL_SUBENTITY
:
5746 case CMD_MODEL_CUSTENTITY
:
5749 newanim
->subentity
= get_cached_model_index(value
);
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);
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);
5769 case CMD_MODEL_UNSUMMONFRAME
:
5770 newanim
->unsummonframe
= GET_INT_ARG(1);
5772 case CMD_MODEL_AT_SCRIPT
:
5774 shutdownmessage
= "command '@script' must follow an animation!";
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
);
5786 scriptbuf
[strlen(scriptbuf
) - strlen(endifid_text
)] = 0; // cut last chars
5787 while(strncmp(buf
+ pos
, "@script", 7)) {
5791 while(strncmp(buf
+ pos
, "@end_script", 11)) {
5792 len
= strlen(scriptbuf
);
5793 scriptbuf
[len
] = *(buf
+ pos
);
5794 scriptbuf
[len
+ 1] = 0;
5798 strcat(scriptbuf
, endifid_text
); // put back last chars
5799 strcat(scriptbuf
, sur_text
); // put back last chars
5801 case CMD_MODEL_AT_CMD
:
5802 //translate @cmd into script function call
5804 shutdownmessage
= "command '@cmd' must follow an animation!";
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
);
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
5827 if(value
&& value
[0]) {
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
5840 if(command
&& command
[0])
5841 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__
, command
, filename
, line
);
5846 pos
+= getNewLineStart(buf
+ pos
);
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'!";
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;
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
) {
5892 newchar
->hostile
= TYPE_PLAYER
;
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
;
5898 newchar
->hostile
= TYPE_ENEMY
| TYPE_PLAYER
;
5900 newchar
->hostile
= 0;
5902 case TYPE_SHOT
: // only target enemies
5903 newchar
->hostile
= TYPE_ENEMY
;
5905 case TYPE_NPC
: // default npc behivior
5906 newchar
->hostile
= TYPE_ENEMY
;
5911 if(newchar
->candamage
< 0) { // not been initialized, so initialize it
5912 switch (newchar
->type
) {
5914 newchar
->candamage
= TYPE_PLAYER
| TYPE_SHOT
;
5915 if(newchar
->subtype
== SUBTYPE_ARROW
)
5916 newchar
->candamage
|= TYPE_OBSTACLE
;
5919 newchar
->candamage
= TYPE_PLAYER
| TYPE_ENEMY
| TYPE_OBSTACLE
;
5922 newchar
->candamage
= TYPE_ENEMY
| TYPE_PLAYER
| TYPE_OBSTACLE
;
5924 newchar
->candamage
= TYPE_PLAYER
| TYPE_ENEMY
| TYPE_OBSTACLE
;
5927 newchar
->candamage
= TYPE_ENEMY
| TYPE_PLAYER
| TYPE_OBSTACLE
;
5930 newchar
->candamage
= TYPE_ENEMY
| TYPE_OBSTACLE
;
5933 newchar
->candamage
= TYPE_PLAYER
;
5938 if(newchar
->projectilehit
< 0) { // not been initialized, so initialize it
5939 switch (newchar
->type
) {
5941 newchar
->projectilehit
= TYPE_ENEMY
| TYPE_PLAYER
| TYPE_OBSTACLE
;
5944 newchar
->projectilehit
= TYPE_ENEMY
| TYPE_PLAYER
| TYPE_OBSTACLE
;
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
;
5951 case TYPE_SHOT
: // hmm, don't really needed
5952 newchar
->projectilehit
= TYPE_ENEMY
| TYPE_PLAYER
| TYPE_OBSTACLE
;
5955 newchar
->projectilehit
= TYPE_ENEMY
| TYPE_PLAYER
| TYPE_OBSTACLE
;
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
);
5979 freeAndNull((void**) &buf
);
5980 freeAndNull((void**) &scriptbuf
);
5982 if(!shutdownmessage
)
5985 shutdown(1, "Fatal Error in load_cached_model, file: %s, line %d, message: %s\n", filename
, line
,
5992 int is_set(s_model
* model
, int m
) { // New function to determine if a freespecial has been set
5995 for(i
= 0; i
< model
->specials_loaded
; i
++) {
5996 if(model
->special
[i
][MAX_SPECIAL_INPUTS
- 2] == m
) {
6004 int load_script_setting() {
6005 char *filename
= "data/script.txt";
6006 char *buf
, *command
;
6010 char argbuf
[MAX_ARG_LEN
+ 1] = "";
6012 if(buffer_pakfile(filename
, &buf
, &size
) != 1)
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);
6045 pos
+= getNewLineStart(buf
+ pos
);
6048 freeAndNull((void**) &buf
);
6052 // Load / cache all models
6054 char filename
[128] = "data/models.txt";
6062 char tmpBuff
[128] = { "" };
6063 int maxanim
= MAX_ANIS
; // temporary counter
6066 char argbuf
[MAX_ARG_LEN
+ 1] = "";
6067 modelstxtCommands cmd
;
6068 int modelLoadCount
= 0;
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);
6079 load_background("data/bgs/loading", 0);
6080 standard_palette(1);
6082 if(isLoadingScreenTypeBar(loadingbg
[0].set
)) {
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
;
6095 if(custModels
!= NULL
) {
6096 strcpy(filename
, "data/");
6097 strcat(filename
, custModels
);
6100 if(buffer_pakfile(filename
, &buf
, &size
) != 1)
6101 shutdown(1, "Error loading model list from %s", filename
);
6104 while(pos
< size
) // peek global settings
6107 ParseArgs(&arglist
, buf
+ pos
, argbuf
);
6108 command
= GET_ARG(0);
6109 cmd
= getModelstxtCommand(modelstxtcmdlist
, command
);
6111 case CMD_MODELSTXT_MAXIDLES
:
6113 dyn_anim_custom_maxvalues
.max_idles
= MAX(GET_INT_ARG(1), MAX_IDLES
);
6115 case CMD_MODELSTXT_MAXWALKS
:
6116 dyn_anim_custom_maxvalues
.max_walks
= MAX(GET_INT_ARG(1), MAX_WALKS
);
6118 case CMD_MODELSTXT_MAXBACKWALKS
:
6119 // max backward walks
6120 dyn_anim_custom_maxvalues
.max_backwalks
= MAX(GET_INT_ARG(1), MAX_BACKWALKS
);
6122 case CMD_MODELSTXT_MAXUPS
:
6124 dyn_anim_custom_maxvalues
.max_ups
= MAX(GET_INT_ARG(1), MAX_UPS
);
6126 case CMD_MODELSTXT_MAXDOWNS
:
6128 dyn_anim_custom_maxvalues
.max_downs
= MAX(GET_INT_ARG(1), MAX_DOWNS
);
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
);
6134 case CMD_MODELSTXT_MAXFOLLOWS
:
6136 dyn_anim_custom_maxvalues
.max_follows
= MAX(GET_INT_ARG(1), MAX_FOLLOWS
);
6138 case CMD_MODELSTXT_MAXFREESPECIALS
:
6140 dyn_anim_custom_maxvalues
.max_freespecials
= MAX(GET_INT_ARG(1), MAX_SPECIALS
);
6142 case CMD_MODELSTXT_MAXATTACKS
:
6143 dyn_anim_custom_maxvalues
.max_attacks
= MAX(GET_INT_ARG(1), MAX_ATTACKS
);
6145 case CMD_MODELSTXT_MUSIC
:
6146 music(GET_ARG(1), 1, atol(GET_ARG(2)));
6148 case CMD_MODELSTXT_LOAD
:
6149 // Add path to cache list
6151 cache_model(GET_ARG(1), GET_ARG(2), 1);
6153 case CMD_MODELSTXT_COLOURSELECT
:
6154 // 6-2-2005 if string for colourselect found
6155 colourselect
= GET_INT_ARG(1); // 6-2-2005
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);
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);
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);
6172 case CMD_MODELSTXT_AJSPECIAL
:
6173 // Flag to determine if a + j executes special
6174 ajspecial
= GET_INT_ARG(1);
6176 case CMD_MODELSTXT_NOCOST
:
6177 // Nocost set in models.txt
6178 nocost
= GET_INT_ARG(1);
6180 case CMD_MODELSTXT_NOCHEATS
:
6181 //disable cheat option in menu
6182 forcecheatsoff
= GET_INT_ARG(1);
6184 case CMD_MODELSTXT_NODROPEN
:
6187 case CMD_MODELSTXT_KNOW
:
6188 // Just add path to cache list
6189 cache_model(GET_ARG(1), GET_ARG(2), 0);
6191 case CMD_MODELSTXT_NOAIRCANCEL
:
6192 noaircancel
= GET_INT_ARG(1);
6194 case CMD_MODELSTXT_NOMAXRUSHRESET
:
6195 nomaxrushreset
[4] = GET_INT_ARG(1);
6197 case CMD_MODELSTXT_MPBLOCK
:
6198 // Take from MP first?
6199 mpblock
= GET_INT_ARG(1);
6201 case CMD_MODELSTXT_BLOCKRATIO
:
6202 // Nullify or reduce damage?
6203 blockratio
= GET_INT_ARG(1);
6205 case CMD_MODELSTXT_NOCHIPDEATH
:
6206 nochipdeath
= GET_INT_ARG(1);
6208 case CMD_MODELSTXT_LIFESCORE
:
6209 lifescore
= GET_INT_ARG(1);
6211 case CMD_MODELSTXT_CREDSCORE
:
6212 // Number of points needed to earn a 1-up
6213 credscore
= GET_INT_ARG(1);
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;
6221 case CMD_MODELSTXT_COMBODELAY
:
6222 combodelay
= GET_INT_ARG(1);
6225 if(command
&& *command
)
6226 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__
, command
, filename
, 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
;
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.
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
);
6275 void unload_levelorder() {
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
]);
6286 strcpy(set_names
[j
], "");
6288 num_difficulties
= 0;
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
)
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
;
6320 // Add a scene to the level order
6321 void add_scene(char *filename
, int diff
) {
6322 if(diff
> MAX_DIFFICULTIES
)
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
;
6333 // Add a select screen file to the level order
6334 void add_select(char *filename
, int diff
) {
6335 if(diff
> MAX_DIFFICULTIES
)
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
;
6346 static void _readbarstatus(char *buf
, s_barstatus
* pstatus
) {
6349 char argbuf
[MAX_ARG_LEN
+ 1] = "";
6351 ParseArgs(&arglist
, buf
, argbuf
);
6352 if((value
= GET_ARG(1))[0])
6353 pstatus
->sizex
= atoi(value
);
6356 if((value
= GET_ARG(2))[0])
6357 pstatus
->sizey
= atoi(value
);
6360 if((value
= GET_ARG(3))[0])
6361 pstatus
->noborder
= atoi(value
);
6364 if((value
= GET_ARG(4))[0])
6365 pstatus
->type
= atoi(value
);
6368 if((value
= GET_ARG(5))[0])
6369 pstatus
->orientation
= atoi(value
);
6372 if((value
= GET_ARG(6))[0])
6373 pstatus
->borderlayer
= atoi(value
);
6376 if((value
= GET_ARG(7))[0])
6377 pstatus
->shadowlayer
= atoi(value
);
6380 if((value
= GET_ARG(8))[0])
6381 pstatus
->barlayer
= atoi(value
);
6384 if((value
= GET_ARG(9))[0])
6385 pstatus
->backlayer
= atoi(value
);
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] = "";
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
6417 char argbuf
[MAX_ARG_LEN
+ 1] = "";
6418 levelOrderCommands cmd
;
6421 unload_levelorder();
6423 if(custLevels
!= NULL
) {
6424 strcpy(filename
, "data/");
6425 strcat(filename
, custLevels
);
6427 strcpy(filename
, "data/levels.txt");
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
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;
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
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;
6491 ParseArgs(&arglist
, buf
+ pos
, argbuf
);
6492 command
= GET_ARG(0);
6493 cmd
= getLevelOrderCommand(levelordercmdlist
, command
);
6495 case CMD_LEVELORDER_BLENDFX
:
6496 for(i
= 0; i
< MAX_BLENDINGS
; i
++) {
6497 if(GET_INT_ARG(i
+ 1))
6504 case CMD_LEVELORDER_SET
:
6505 if(num_difficulties
>= MAX_DIFFICULTIES
) {
6506 errormessage
= "Too many sets of levels (check MAX_DIFFICULTIES)!";
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
6516 case CMD_LEVELORDER_IFCOMPLETE
:
6518 ifcomplete
[current_set
] = GET_INT_ARG(1);
6520 case CMD_LEVELORDER_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
);
6535 case CMD_LEVELORDER_FILE
:
6537 strncpy(value
, GET_ARG(1), 127);
6538 add_level(value
, current_set
);
6540 case CMD_LEVELORDER_SCENE
:
6542 strncpy(value
, GET_ARG(1), 127);
6543 add_scene(value
, current_set
);
6545 case CMD_LEVELORDER_SELECT
:
6547 strncpy(value
, GET_ARG(1), 127);
6548 add_select(value
, current_set
);
6550 case CMD_LEVELORDER_NEXT
:
6552 // Set 'gonext' flag of last loaded level
6553 if(num_levels
[current_set
] < 1) {
6554 errormessage
= "Error in level order (next before file)!";
6557 levelorder
[current_set
][num_levels
[current_set
] - 1]->gonext
= 1;
6559 case CMD_LEVELORDER_END
:
6561 // Set endgame flag of last loaded level
6562 if(num_levels
[current_set
] < 1) {
6563 errormessage
= "Error in level order (next before file)!";
6566 levelorder
[current_set
][num_levels
[current_set
] - 1]->gonext
= 2;
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
6572 difflives
[current_set
] = GET_INT_ARG(1);
6574 case CMD_LEVELORDER_DISABLEHOF
:
6576 noshowhof
[current_set
] = GET_INT_ARG(1);
6578 case CMD_LEVELORDER_CANSAVE
:
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
6584 cansave_flag
[current_set
] = GET_INT_ARG(1);
6586 case CMD_LEVELORDER_Z
:
6587 // 2-10-05 adjust the walkable coordinates
6589 z_coords
[0] = GET_INT_ARG(1);
6590 z_coords
[1] = GET_INT_ARG(2);
6591 z_coords
[2] = GET_INT_ARG(3);
6593 case CMD_LEVELORDER_BRANCH
:
6594 // 2007-2-22 level branch name
6596 strncpy(branch_name
, GET_ARG(1), MAX_NAME_LEN
);
6598 case CMD_LEVELORDER_P1LIFE
:
6599 case CMD_LEVELORDER_P2LIFE
:
6600 case CMD_LEVELORDER_P3LIFE
:
6601 case CMD_LEVELORDER_P4LIFE
:
6603 case CMD_LEVELORDER_P1LIFE
:
6606 case CMD_LEVELORDER_P2LIFE
:
6609 case CMD_LEVELORDER_P3LIFE
:
6613 case CMD_LEVELORDER_P4LIFE
:
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
);
6625 case CMD_LEVELORDER_P1MP
:
6626 case CMD_LEVELORDER_P2MP
:
6627 case CMD_LEVELORDER_P3MP
:
6628 case CMD_LEVELORDER_P4MP
:
6630 case CMD_LEVELORDER_P1MP
:
6633 case CMD_LEVELORDER_P2MP
:
6636 case CMD_LEVELORDER_P3MP
:
6639 case CMD_LEVELORDER_P4MP
:
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
);
6651 case CMD_LEVELORDER_P1LIFEX
:
6652 case CMD_LEVELORDER_P2LIFEX
:
6653 case CMD_LEVELORDER_P3LIFEX
:
6654 case CMD_LEVELORDER_P4LIFEX
:
6656 case CMD_LEVELORDER_P1LIFEX
:
6659 case CMD_LEVELORDER_P2LIFEX
:
6662 case CMD_LEVELORDER_P3LIFEX
:
6665 case CMD_LEVELORDER_P4LIFEX
:
6671 for(i
= 0; i
< 3; i
++)
6672 if((arg
= GET_ARG(i
+ 1))[0])
6673 plifeX
[j
][i
] = atoi(arg
);
6676 case CMD_LEVELORDER_P1LIFEN
:
6677 case CMD_LEVELORDER_P2LIFEN
:
6678 case CMD_LEVELORDER_P3LIFEN
:
6679 case CMD_LEVELORDER_P4LIFEN
:
6681 case CMD_LEVELORDER_P1LIFEN
:
6684 case CMD_LEVELORDER_P2LIFEN
:
6687 case CMD_LEVELORDER_P3LIFEN
:
6690 case CMD_LEVELORDER_P4LIFEN
:
6696 for(i
= 0; i
< 3; i
++)
6697 if((arg
= GET_ARG(i
+ 1))[0])
6698 plifeN
[j
][i
] = atoi(arg
);
6701 case CMD_LEVELORDER_E1LIFE
:
6702 case CMD_LEVELORDER_E2LIFE
:
6703 case CMD_LEVELORDER_E3LIFE
:
6704 case CMD_LEVELORDER_E4LIFE
:
6706 case CMD_LEVELORDER_E1LIFE
:
6709 case CMD_LEVELORDER_E2LIFE
:
6712 case CMD_LEVELORDER_E3LIFE
:
6716 case CMD_LEVELORDER_E4LIFE
:
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
);
6728 case CMD_LEVELORDER_P1ICON
:
6729 case CMD_LEVELORDER_P2ICON
:
6730 case CMD_LEVELORDER_P3ICON
:
6731 case CMD_LEVELORDER_P4ICON
:
6733 case CMD_LEVELORDER_P1ICON
:
6736 case CMD_LEVELORDER_P2ICON
:
6739 case CMD_LEVELORDER_P3ICON
:
6743 case CMD_LEVELORDER_P4ICON
:
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
);
6755 case CMD_LEVELORDER_P1ICONW
:
6756 case CMD_LEVELORDER_P2ICONW
:
6757 case CMD_LEVELORDER_P3ICONW
:
6758 case CMD_LEVELORDER_P4ICONW
:
6760 case CMD_LEVELORDER_P1ICONW
:
6763 case CMD_LEVELORDER_P2ICONW
:
6766 case CMD_LEVELORDER_P3ICONW
:
6770 case CMD_LEVELORDER_P4ICONW
:
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
);
6782 case CMD_LEVELORDER_MP1ICON
:
6783 case CMD_LEVELORDER_MP2ICON
:
6784 case CMD_LEVELORDER_MP3ICON
:
6785 case CMD_LEVELORDER_MP4ICON
:
6787 case CMD_LEVELORDER_MP1ICON
:
6790 case CMD_LEVELORDER_MP2ICON
:
6793 case CMD_LEVELORDER_MP3ICON
:
6796 case CMD_LEVELORDER_MP4ICON
:
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
);
6807 case CMD_LEVELORDER_P1NAMEJ
:
6808 case CMD_LEVELORDER_P2NAMEJ
:
6809 case CMD_LEVELORDER_P3NAMEJ
:
6810 case CMD_LEVELORDER_P4NAMEJ
:
6812 case CMD_LEVELORDER_P1NAMEJ
:
6815 case CMD_LEVELORDER_P2NAMEJ
:
6818 case CMD_LEVELORDER_P3NAMEJ
:
6821 case CMD_LEVELORDER_P4NAMEJ
:
6827 for(i
= 0; i
< 7; i
++)
6828 if((arg
= GET_ARG(i
+ 1))[0])
6829 pnameJ
[j
][i
] = atoi(arg
);
6832 case CMD_LEVELORDER_P1SCORE
:
6833 case CMD_LEVELORDER_P2SCORE
:
6834 case CMD_LEVELORDER_P3SCORE
:
6835 case CMD_LEVELORDER_P4SCORE
:
6837 case CMD_LEVELORDER_P1SCORE
:
6840 case CMD_LEVELORDER_P2SCORE
:
6843 case CMD_LEVELORDER_P3SCORE
:
6846 case CMD_LEVELORDER_P4SCORE
:
6852 for(i
= 0; i
< 7; i
++)
6853 if((arg
= GET_ARG(i
+ 1))[0])
6854 pscore
[j
][i
] = atoi(arg
);
6857 case CMD_LEVELORDER_P1SHOOT
:
6858 case CMD_LEVELORDER_P2SHOOT
:
6859 case CMD_LEVELORDER_P3SHOOT
:
6860 case CMD_LEVELORDER_P4SHOOT
:
6862 case CMD_LEVELORDER_P1SHOOT
:
6865 case CMD_LEVELORDER_P2SHOOT
:
6868 case CMD_LEVELORDER_P3SHOOT
:
6871 case CMD_LEVELORDER_P4SHOOT
:
6877 for(i
= 0; i
< 3; i
++)
6878 if((arg
= GET_ARG(i
+ 1))[0])
6879 pshoot
[j
][i
] = atoi(arg
);
6881 case CMD_LEVELORDER_P1RUSH
:
6882 case CMD_LEVELORDER_P2RUSH
:
6883 case CMD_LEVELORDER_P3RUSH
:
6884 case CMD_LEVELORDER_P4RUSH
:
6886 case CMD_LEVELORDER_P1RUSH
:
6889 case CMD_LEVELORDER_P2RUSH
:
6892 case CMD_LEVELORDER_P3RUSH
:
6895 case CMD_LEVELORDER_P4RUSH
:
6901 for(i
= 0; i
< 8; i
++)
6902 if((arg
= GET_ARG(i
+ 1))[0])
6903 prush
[j
][i
] = atoi(arg
);
6905 case CMD_LEVELORDER_E1ICON
:
6906 case CMD_LEVELORDER_E2ICON
:
6907 case CMD_LEVELORDER_E3ICON
:
6908 case CMD_LEVELORDER_E4ICON
:
6910 case CMD_LEVELORDER_E1ICON
:
6913 case CMD_LEVELORDER_E2ICON
:
6916 case CMD_LEVELORDER_E3ICON
:
6920 case CMD_LEVELORDER_E4ICON
:
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
);
6932 case CMD_LEVELORDER_E1NAME
:
6933 case CMD_LEVELORDER_E2NAME
:
6934 case CMD_LEVELORDER_E3NAME
:
6935 case CMD_LEVELORDER_E4NAME
:
6937 case CMD_LEVELORDER_E1NAME
:
6940 case CMD_LEVELORDER_E2NAME
:
6943 case CMD_LEVELORDER_E3NAME
:
6946 case CMD_LEVELORDER_E4NAME
:
6952 for(i
= 0; i
< 3; i
++)
6953 if((arg
= GET_ARG(i
+ 1))[0])
6954 ename
[j
][i
] = atoi(arg
);
6957 case CMD_LEVELORDER_P1SMENU
:
6958 case CMD_LEVELORDER_P2SMENU
:
6959 case CMD_LEVELORDER_P3SMENU
:
6960 case CMD_LEVELORDER_P4SMENU
:
6962 case CMD_LEVELORDER_P1SMENU
:
6965 case CMD_LEVELORDER_P2SMENU
:
6968 case CMD_LEVELORDER_P3SMENU
:
6971 case CMD_LEVELORDER_P4SMENU
:
6977 for(i
= 0; i
< 4; i
++)
6978 if((arg
= GET_ARG(i
+ 1))[0])
6979 psmenu
[j
][i
] = atoi(arg
);
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
);
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
);
6999 bgicon_offsets
[2] = HUD_Z
/ 2;
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
);
7011 olicon_offsets
[2] = HUD_Z
* 3;
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
);
7018 case CMD_LEVELORDER_LBARSIZE
:
7019 _readbarstatus(buf
+ pos
, &lbarstatus
);
7021 case CMD_LEVELORDER_OLBARSIZE
:
7022 _readbarstatus(buf
+ pos
, &olbarstatus
);
7024 case CMD_LEVELORDER_MPBARSIZE
:
7025 _readbarstatus(buf
+ pos
, &mpbarstatus
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
7067 case CMD_LEVELORDER_MUSICOVERLAP
:
7069 diffoverlap
[current_set
] = GET_INT_ARG(1);
7071 case CMD_LEVELORDER_SHOWRUSHBONUS
:
7074 case CMD_LEVELORDER_NOSLOWFX
:
7077 case CMD_LEVELORDER_EQUALAIRPAUSE
:
7080 case CMD_LEVELORDER_HISCOREBG
:
7083 case CMD_LEVELORDER_COMPLETEBG
:
7086 case CMD_LEVELORDER_LOADINGBG
:
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),
7094 case CMD_LEVELORDER_LOADINGBG2
:
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),
7102 case CMD_LEVELORDER_LOADINGMUSIC
:
7103 loadingmusic
= GET_INT_ARG(1);
7105 case CMD_LEVELORDER_UNLOCKBG
:
7108 case CMD_LEVELORDER_NOSHARE
:
7111 case CMD_LEVELORDER_CUSTFADE
:
7112 //8-2-2005 custom fade
7114 custfade
[current_set
] = GET_INT_ARG(1);
7116 case CMD_LEVELORDER_CONTINUESCORE
:
7117 //8-2-2005 custom fade end
7120 continuescore
[current_set
] = GET_INT_ARG(1);
7122 case CMD_LEVELORDER_CREDITS
:
7124 diffcreds
[current_set
] = GET_INT_ARG(1);
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
7129 typemp
[current_set
] = GET_INT_ARG(1);
7131 case CMD_LEVELORDER_SINGLE
:
7132 if(current_set
< 0) {
7133 for(i
= 0; i
< MAX_DIFFICULTIES
; i
++)
7134 maxplayers
[i
] = ctrlmaxplayers
[i
] = 1;
7136 maxplayers
[current_set
] = ctrlmaxplayers
[current_set
] = 1;
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];
7146 maxplayers
[current_set
] = ctrlmaxplayers
[current_set
] = GET_INT_ARG(1);
7149 case CMD_LEVELORDER_NOSAME
:
7151 same
[current_set
] = GET_INT_ARG(1);
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);
7163 case CMD_LEVELORDER_MAXWALLHEIGHT
:
7164 MAX_WALL_HEIGHT
= GET_INT_ARG(1);
7165 if(MAX_WALL_HEIGHT
< 0)
7166 MAX_WALL_HEIGHT
= 1000;
7168 case CMD_LEVELORDER_SCOREFORMAT
:
7169 scoreformat
= GET_INT_ARG(1);
7172 if(command
&& command
[0])
7173 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__
, command
, filename
, line
);
7177 pos
+= getNewLineStart(buf
+ pos
);
7183 // Variables without defaults will be auto populated.
7184 if(olbarstatus
.sizex
== 0) {
7185 olbarstatus
= lbarstatus
;
7189 plife
[2][0] = plife
[0][0];
7190 plife
[2][1] = plife
[2][1] + (plife
[0][1] - 10);
7193 plife
[3][0] = plife
[1][0];
7194 plife
[3][1] = plife
[3][1] + (plife
[1][1] - 10);
7198 elife
[2][0] = elife
[0][0];
7199 elife
[2][1] = elife
[2][1] + (elife
[0][1] - 27);
7202 elife
[3][0] = elife
[1][0];
7203 elife
[3][1] = elife
[3][1] + (elife
[1][1] - 27);
7207 picon
[2][0] = picon
[0][0];
7208 picon
[2][1] = picon
[2][1] + (picon
[0][1] - 2);
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);
7225 eicon
[2][0] = eicon
[0][0];
7226 eicon
[2][1] = eicon
[2][1] + (eicon
[0][1] - 19);
7229 eicon
[3][0] = eicon
[1][0];
7230 eicon
[3][1] = eicon
[3][1] + (eicon
[1][1] - 19);
7234 pmp
[0][0] = plife
[0][0];
7235 pmp
[0][1] = plife
[0][1] + 8;
7238 pmp
[1][0] = plife
[1][0];
7239 pmp
[1][1] = plife
[1][1] + 8;
7242 pmp
[2][0] = pmp
[0][0];
7243 pmp
[2][1] = pmp
[2][1] + (pmp
[0][1] - 18);
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)
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)
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)
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)
7335 ename
[0][0] = elife
[0][0] + 1;
7336 ename
[0][1] = eicon
[0][1];
7339 ename
[1][0] = elife
[1][0] + 1;
7340 ename
[1][1] = eicon
[1][1];
7343 ename
[2][0] = ename
[0][0];
7344 ename
[2][1] = eicon
[2][1];
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)
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)
7359 if(timeloc
[5] == -1)
7363 errormessage
= "No levels were loaded!";
7366 freeAndNull((void**) &buf
);
7369 shutdown(1, "load_levelorder ERROR in %s at %d, msg: %s\n", filename
, line
, errormessage
);
7373 void free_level(s_level
* lv
) {
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
;
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
]);
7386 for(i
= 1; i
< lv
->numbglayers
; i
++)
7387 freeAndNull((void**) &lv
->bglayers
[i
].handle
);
7390 for(i
= 0; i
< lv
->numfglayers
; i
++)
7391 freeAndNull((void**) &lv
->fglayers
[i
].handle
);
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
);
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
;
7416 templistnode
= templistnode2
;
7421 tempnode
= lv
->spawn_script_cache_head
;
7422 lv
->spawn_script_cache_head
= NULL
;
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
;
7430 tempnode
= tempnode2
;
7432 freeAndNull((void**) &lv
);
7436 void unload_level() {
7438 unload_background();
7441 freescreen(&bgbuffer
);
7446 level
->advancetime
= 0;
7448 level
->quaketime
= 0;
7451 printf("Level Unloading: '%s'\n", level
->name
);
7452 freeAndNull((void**) &level
->name
);
7455 temp
= getFirstModel();
7461 temp
= getCurrentModel();
7463 temp
= getNextModel();
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
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
;
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
;
7509 while(templistnode
->next
) {
7510 templistnode
= templistnode
->next
;
7512 templistnode
->next
= malloc(sizeof(s_spawn_script_list_node
));
7513 templistnode
= templistnode
->next
;
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
;
7522 if(stricmp(value
, tempnode
->filename
) == 0) {
7523 templistnode
->spawn_script
= tempnode
->cached_spawn_script
;
7527 tempnode
= tempnode
->next
;
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);
7538 result
= "Multiple spawn entry script!";
7542 if(load_script(templistnode
->spawn_script
, value
)) {
7543 Script_Compile(templistnode
->spawn_script
);
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
;
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
;
7557 result
= "Failed loading spawn entry script!";
7565 static const s_hole default_hole_coords
= {
7575 void load_level(char *filename
) {
7578 ptrdiff_t pos
, oldpos
;
7581 char string
[128] = { "" };
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;
7593 char argbuf
[MAX_ARG_LEN
+ 1] = "";
7596 char argbuf2
[MAX_ARG_LEN
+ 1] = "";
7601 char *errormessage
= NULL
;
7602 char *scriptname
= NULL
;
7603 Script
*tempscript
= NULL
;
7604 s_panel_filenames panel_filenames
;
7608 printf("Level Loading: '%s'\n", filename
);
7610 if(isLoadingScreenTypeBg(loadingbg
[1].set
)) {
7612 strcpy(string
, custBkgrds
);
7613 strcat(string
, "loading2");
7614 load_background(string
, 0);
7616 load_cached_background("data/bgs/loading2", 0);
7618 clearscreen(vscreen
);
7620 standard_palette(1);
7623 if(isLoadingScreenTypeBar(loadingbg
[1].set
)) {
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
));
7635 errormessage
= (char*) E_OUT_OF_MEMORY
;
7638 level
->name
= strdup(filename
);
7643 if(buffer_pakfile(filename
, &buf
, &size
) != 1) {
7644 errormessage
= "Unable to load level file!";
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
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;
7661 blendfx
[BLEND_MULTIPLY
] = 1;
7668 reset_playable_list(1);
7670 // Now interpret the contents of buf line by line
7673 ParseArgs(&arglist
, buf
+ pos
, argbuf
);
7674 command
= GET_ARG(0);
7675 cmd
= getLevelCommand(levelcmdlist
, command
);
7677 case CMD_LEVEL_LOADINGBG
:
7678 load_background(GET_ARG(1), 0);
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),
7685 standard_palette(1);
7688 update_loading(&bgPosi
, -1, 1); // initialize the update screen
7690 case CMD_LEVEL_MUSICFADE
:
7691 memset(&next
, 0, sizeof(s_spawn_entry
));
7692 next
.musicfade
= GET_FLOAT_ARG(1);
7694 case CMD_LEVEL_MUSIC
:
7696 strncpy(string
, value
, 128);
7697 musicOffset
= atol(GET_ARG(2));
7699 music(string
, 1, musicOffset
);
7704 pos
+= getNewLineStart(buf
+ pos
);
7705 #define GET_ARG2(z) arglist2.count > z ? arglist2.args[z] : ""
7707 ParseArgs(&arglist2
, buf
+ pos
, argbuf2
);
7708 command
= GET_ARG2(0);
7709 cmd2
= getLevelCommand(levelcmdlist
, command
);
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
;
7719 strncpy(musicPath
, string
, 128);
7725 case CMD_LEVEL_ALLOWSELECT
:
7726 load_playable_list(buf
+ pos
);
7728 case CMD_LEVEL_LOAD
:
7730 printf("load_level: load %s, %s\n", GET_ARG(1), filename
);
7732 tempmodel
= findmodel(GET_ARG(1));
7734 load_cached_model(GET_ARG(1), filename
, GET_INT_ARG(2));
7736 update_model_loadflag(tempmodel
, GET_INT_ARG(2));
7738 case CMD_LEVEL_BACKGROUND
:
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
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;
7774 case CMD_LEVEL_BGLAYER
:
7775 if(level
->numbglayers
>= LEVEL_MAX_BGLAYERS
) {
7776 errormessage
= "Too many bg layers in level (check LEVEL_MAX_BGLAYERS)!";
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
++;
7815 case CMD_LEVEL_FGLAYER
:
7816 if(level
->numfglayers
>= LEVEL_MAX_FGLAYERS
) {
7817 errormessage
= "Too many bg layers in level (check LEVEL_MAX_FGLAYERS)!";
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
++;
7856 case CMD_LEVEL_WATER
:
7857 load_texture(GET_ARG(1));
7861 texture_set_wave((float) i
);
7863 case CMD_LEVEL_DIRECTION
:
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
;
7886 case CMD_LEVEL_FACING
:
7887 level
->facing
= GET_INT_ARG(1);
7889 case CMD_LEVEL_ROCK
:
7890 level
->rocking
= GET_INT_ARG(1);
7892 case CMD_LEVEL_BGSPEED
:
7893 level
->bgspeed
= GET_FLOAT_ARG(1);
7895 level
->bgspeed
*= -1;
7897 case CMD_LEVEL_MIRROR
:
7898 level
->mirror
= GET_INT_ARG(1);
7900 case CMD_LEVEL_BOSSMUSIC
:
7901 strncpy(level
->bossmusic
, GET_ARG(1), 255);
7902 level
->bossmusic_offset
= atol(GET_ARG(2));
7904 case CMD_LEVEL_NOPAUSE
:
7905 nopause
= GET_INT_ARG(1);
7907 case CMD_LEVEL_NOSCREENSHOT
:
7908 noscreenshot
= GET_INT_ARG(1);
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
7917 case CMD_LEVEL_SETWEAP
:
7918 // Specify a weapon for each level
7919 level
->setweap
= GET_INT_ARG(1);
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);
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);
7929 case CMD_LEVEL_NOSLOW
:
7930 // If set, level will not slow down when bosses are defeated
7931 level
->noslow
= GET_INT_ARG(1);
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)
7938 case CMD_LEVEL_NOHIT
:
7939 level
->nohit
= GET_INT_ARG(1);
7941 case CMD_LEVEL_GRAVITY
:
7942 level
->gravity
= GET_FLOAT_ARG(1);
7943 level
->gravity
/= 100;
7945 case CMD_LEVEL_MAXFALLSPEED
:
7946 level
->maxfallspeed
= GET_FLOAT_ARG(1);
7947 level
->maxfallspeed
/= 10;
7949 case CMD_LEVEL_MAXTOSSSPEED
:
7950 level
->maxtossspeed
= GET_FLOAT_ARG(1);
7951 level
->maxtossspeed
/= 10;
7953 case CMD_LEVEL_CAMERATYPE
:
7954 cameratype
= GET_INT_ARG(1);
7956 case CMD_LEVEL_CAMERAOFFSET
:
7957 level
->cameraxoffset
= GET_INT_ARG(1);
7958 level
->camerazoffset
= GET_INT_ARG(2);
7960 case CMD_LEVEL_SPAWN1
:
7961 case CMD_LEVEL_SPAWN2
:
7962 case CMD_LEVEL_SPAWN3
:
7963 case CMD_LEVEL_SPAWN4
:
7965 case CMD_LEVEL_SPAWN1
:
7968 case CMD_LEVEL_SPAWN2
:
7971 case CMD_LEVEL_SPAWN3
:
7974 case CMD_LEVEL_SPAWN4
:
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;
7989 case CMD_LEVEL_FRONTPANEL
:
7991 if(!loadfrontpanel(value
))
7992 shutdown(1, "Unable to load '%s'!", value
);
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!";
8004 case CMD_LEVEL_STAGENUMBER
:
8005 current_stage
= GET_INT_ARG(1);
8007 case CMD_LEVEL_ORDER
:
8009 if(panels_loaded
< 1) {
8010 errormessage
= "You must load the panels before entering the level layout!";
8016 while(value
[i
] && level
->numpanels
< LEVEL_MAX_PANELS
) {
8019 if(j
>= 'A' && j
<= 'Z')
8021 else if(j
>= 'a' && j
<= 'z')
8024 errormessage
= "Illegal character in panel order!";
8028 if(j
>= panels_loaded
) {
8030 "Illegal panel index, index is bigger than number of loaded panels.";
8034 level
->order
[level
->numpanels
] = j
;
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
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)!";
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
];
8061 case CMD_LEVEL_WALL
:
8062 if(level
->numwalls
>= LEVEL_MAX_WALLS
) {
8063 errormessage
= "Too many walls in level (check LEVEL_MAX_WALLS)!";
8066 for(i
= 0; i
< sizeof(s_wall
) / sizeof(float); i
++) {
8067 ((float*) &level
->walls
[level
->numwalls
])[i
] = GET_FLOAT_ARG(i
+ 1);
8071 case CMD_LEVEL_PALETTE
:
8072 if(level
->numpalettes
>= LEVEL_MAX_PALETTES
) {
8073 errormessage
= "Too many palettes in level (check LEVEL_MAX_PALETTES)!";
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
;
8084 level
->numpalettes
++;
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
:
8092 case CMD_LEVEL_UPDATESCRIPT
:
8093 tempscript
= &(level
->update_script
);
8094 scriptname
= "levelupdatescript";
8096 case CMD_LEVEL_UPDATEDSCRIPT
:
8097 tempscript
= &(level
->updated_script
);
8098 scriptname
= "levelupdatedscript";
8100 case CMD_LEVEL_KEYSCRIPT
:
8101 tempscript
= &(level
->key_script
);
8102 scriptname
= "levelkeyscript";
8104 case CMD_LEVEL_LEVELSCRIPT
:
8105 tempscript
= &(level
->level_script
);
8106 scriptname
= command
;
8108 case CMD_LEVEL_ENDLEVELSCRIPT
:
8109 tempscript
= &(level
->endlevel_script
);
8110 scriptname
= command
;
8117 if(!Script_IsInitialized(tempscript
))
8118 Script_Init(tempscript
, scriptname
, 1);
8120 errormessage
= "Multiple level script!";
8123 if(load_script(tempscript
, value
))
8124 Script_Compile(tempscript
);
8126 errormessage
= "Failed loading script!";
8130 case CMD_LEVEL_BLOCKED
:
8131 level
->exit_blocked
= GET_INT_ARG(1);
8133 case CMD_LEVEL_ENDHOLE
:
8134 level
->exit_hole
= GET_INT_ARG(1);
8136 case CMD_LEVEL_WAIT
:
8137 // Clear spawn thing, set wait state instead
8138 memset(&next
, 0, sizeof(s_spawn_entry
));
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
));
8147 case CMD_LEVEL_SHADOWCOLOR
:
8148 memset(&next
, 0, sizeof(s_spawn_entry
));
8149 next
.shadowcolor
= GET_INT_ARG(1);
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;
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)
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;
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)
8183 case CMD_LEVEL_SETPALETTE
:
8184 // change system palette
8185 memset(&next
, 0, sizeof(s_spawn_entry
));
8186 next
.palette
= GET_INT_ARG(1);
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)
8195 if(next
.groupmin
< 1)
8196 next
.groupmin
= 100;
8198 case CMD_LEVEL_SPAWN
:
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));
8207 printf("load_level: spawn %s, %s, cached: %p\n", GET_ARG(1), filename
, cached_model
);
8210 tempmodel
= cached_model
;
8212 tempmodel
= load_cached_model(GET_ARG(1), filename
, 0);
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).
8220 case CMD_LEVEL_2PSPAWN
:
8221 // Entity only for 2p game
8222 next
.spawnplayer_count
= 1;
8224 case CMD_LEVEL_3PSPAWN
:
8225 // Entity only for 3p game
8226 next
.spawnplayer_count
= 2;
8228 case CMD_LEVEL_4PSPAWN
:
8229 // Entity only for 4p game
8230 next
.spawnplayer_count
= 3;
8232 case CMD_LEVEL_BOSS
:
8233 next
.boss
= GET_INT_ARG(1);
8234 level
->bosses
+= next
.boss
? 1 : 0;
8236 case CMD_LEVEL_FLIP
:
8237 next
.flip
= GET_INT_ARG(1);
8239 case CMD_LEVEL_HEALTH
:
8240 next
.health
[0] = next
.health
[1] = next
.health
[2] = next
.health
[3] = GET_INT_ARG(1);
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);
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
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
8255 // mp values to put max mp for player by tails
8256 next
.mp
= GET_INT_ARG(1);
8258 case CMD_LEVEL_SCORE
:
8259 // So score can be overriden in the levels .txt file
8260 next
.score
= GET_INT_ARG(1);
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
8267 case CMD_LEVEL_NOLIFE
:
8268 // Flag to determine if entity life is shown when hit
8269 next
.nolife
= GET_INT_ARG(1);
8271 case CMD_LEVEL_ALIAS
:
8272 // Alias (name displayed) of entry to be spawned
8273 strncpy(next
.alias
, GET_ARG(1), MAX_NAME_LEN
);
8276 // Colourmap for new entry
8277 next
.colourmap
= GET_INT_ARG(1);
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;
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);
8291 case CMD_LEVEL_ITEM
:
8292 case CMD_LEVEL_2PITEM
:
8293 case CMD_LEVEL_3PITEM
:
8294 case CMD_LEVEL_4PITEM
:
8296 // Item to be contained by new entry
8297 case CMD_LEVEL_ITEM
:
8298 next
.itemplayer_count
= 0;
8300 case CMD_LEVEL_2PITEM
:
8301 next
.itemplayer_count
= 1;
8303 case CMD_LEVEL_3PITEM
:
8304 next
.itemplayer_count
= 2;
8306 case CMD_LEVEL_4PITEM
:
8307 next
.itemplayer_count
= 3;
8312 // Load model (if not loaded already)
8313 cached_model
= findmodel(GET_ARG(1));
8315 tempmodel
= cached_model
;
8317 tempmodel
= load_cached_model(GET_ARG(1), filename
, 0);
8319 next
.item
= tempmodel
->name
;
8320 next
.itemindex
= get_cached_model_index(next
.item
);
8323 case CMD_LEVEL_ITEMMAP
:
8324 next
.itemmap
= GET_INT_ARG(1);
8326 case CMD_LEVEL_ITEMHEALTH
:
8327 next
.itemhealth
= GET_INT_ARG(1);
8329 case CMD_LEVEL_ITEMALIAS
:
8330 strncpy(next
.itemalias
, GET_ARG(1), MAX_NAME_LEN
);
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));
8337 tempmodel
= cached_model
;
8339 tempmodel
= load_cached_model(GET_ARG(1), filename
, 0);
8341 next
.weapon
= tempmodel
->name
;
8342 next
.weaponindex
= get_cached_model_index(next
.weapon
);
8345 case CMD_LEVEL_AGGRESSION
:
8346 // Aggression can be set per spawn.
8347 next
.aggression
= next
.aggression
+ GET_INT_ARG(1);
8349 case CMD_LEVEL_CREDIT
:
8350 next
.credit
= GET_INT_ARG(1);
8352 case CMD_LEVEL_ITEMTRANS
:
8353 case CMD_LEVEL_ITEMALPHA
:
8354 next
.itemtrans
= GET_INT_ARG(1);
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);
8361 case CMD_LEVEL_SPAWNSCRIPT
:
8362 errormessage
= llHandleCommandSpawnscript(&arglist
, &next
);
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)";
8375 memcpy(&level
->spawnpoints
[level
->numspawns
], &next
, sizeof(s_spawn_entry
));
8379 memset(&next
, 0, sizeof(s_spawn_entry
));
8382 if(command
&& command
[0])
8383 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__
, command
, filename
, line
);
8387 pos
+= getNewLineStart(buf
+ pos
);
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]);
8394 update_loading(&loadingbg
[1], pos
, size
);
8397 if(level
->numpanels
< 1) {
8398 errormessage
= "Level error: level has no panels";
8403 clearscreen(vscreen
);
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
;
8411 if(level
->numbglayers
> 1)
8412 level
->bglayers
[0] = level
->bglayers
[--level
->numbglayers
];
8414 level
->numbglayers
= 0;
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;
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
);
8438 printf("Level Loaded: '%s'\n", level
->name
);
8441 freeAndNull((void**) &buf
);
8444 shutdown(1, "ERROR: load_level, file %s, line %d, message: %s", filename
, line
, errormessage
);
8451 /////////////////////////////////////////////////////////////////////////////
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
;
8468 if(value
> maxvalue
)
8471 if(pstatus
->type
== valuebar
) {
8475 if(value
<= max
/ 4) {
8478 } else if(value
<= max
/ 2) {
8481 } else if(value
<= max
) {
8485 colourindex
= value
/ (max
+ 1) + 3;
8486 bgindex
= colourindex
- 1;
8488 if(colourindex
> 10)
8489 colourindex
= bgindex
= 10;
8492 bgindex
= value
> max
? 5 : 1;
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
;
8505 alphabg
= BLEND_MULTIPLY
+ 1;
8509 if(pstatus
->orientation
== horizontalbar
) {
8510 forex
= pstatus
->direction
? (x
+ max
- len
) : x
;
8514 bkh
= foreh
= pstatus
->sizey
;
8515 } else if(pstatus
->orientation
== verticalbar
) {
8517 forey
= pstatus
->direction
? y
: (y
+ max
- len
);
8518 bkw
= forew
= pstatus
->sizex
;
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
],
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.
8544 int pauselector
= 0;
8550 _menutextm(3, -2, 0, "Pause");
8551 _menutextm((pauselector
== 0), -1, 0, "Continue");
8552 _menutextm((pauselector
== 1), 0, 0, "End Game");
8556 if(bothnewkeys
& (FLAG_MOVEUP
| FLAG_MOVEDOWN
)) {
8558 sound_play_sample(samples
.beep
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
8560 if(bothnewkeys
& FLAG_START
) {
8562 player
[0].lives
= player
[1].lives
= player
[2].lives
= player
[3].lives
= 0; //4player
8566 sound_pause_music(0);
8567 sound_play_sample(samples
.beep2
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
8570 if(bothnewkeys
& FLAG_ESC
) {
8572 sound_pause_music(0);
8573 sound_play_sample(samples
.beep2
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
8576 if(bothnewkeys
& FLAG_SCREENSHOT
) {
8578 sound_pause_music(1);
8579 sound_play_sample(samples
.beep2
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
8588 unsigned getFPS(void) {
8589 static unsigned lasttick
= 0, framerate
= 0;
8590 unsigned curtick
= timer_gettick();
8591 if(lasttick
> curtick
)
8593 framerate
= (framerate
+ (curtick
- lasttick
)) / 2;
8598 return ((10000000 / framerate
) + 9) / 10;
8600 return ((10000000 / framerate
) + 9) / 10000;
8604 void predrawstatus() {
8612 s_model
*model
= NULL
;
8613 s_drawmethod drawmethod
= plainmethod
;
8616 spriteq_add_sprite(videomodes
.hShift
+ bgicon_offsets
[0], savedata
.windowpos
+ bgicon_offsets
[1],
8617 bgicon_offsets
[2], bgicon
, NULL
, 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
++) {
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
);
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
;
8645 icon
= player
[i
].ent
->modeldata
.icon
;
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",
8707 font_printf(videomodes
.shiftpos
[i
] + prush
[i
][2], prush
[i
][3], rush
[3], 0, "%i",
8708 player
[i
].ent
->rush
[0]);
8711 font_printf(videomodes
.shiftpos
[i
] + prush
[i
][4], prush
[i
][5], rush
[4], 0, "%s",
8713 font_printf(videomodes
.shiftpos
[i
] + prush
[i
][6], prush
[i
][7], rush
[5], 0, "%i",
8714 player
[i
].ent
->rush
[1]);
8719 font_printf(videomodes
.shiftpos
[i
] + prush
[i
][4], prush
[i
][5], rush
[4], 0, "%s",
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
;
8735 icon
= player
[i
].ent
->opponent
->modeldata
.icon
;
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
);
8747 font_printf(videomodes
.shiftpos
[i
] + pnameJ
[i
][2], savedata
.windowpos
+ pnameJ
[i
][3],
8748 pnameJ
[i
][6], 0, "Please Wait");
8750 font_printf(videomodes
.shiftpos
[i
] + pnameJ
[i
][2], savedata
.windowpos
+ pnameJ
[i
][3],
8751 pnameJ
[i
][6], 0, "Select Hero");
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
,
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
8768 for(x
= 0; x
< maxplayers
[current_set
]; x
++) {
8769 if((i
!= x
) && (!strcmp(player
[i
].name
, player
[x
].name
))) {
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
;
8783 execute_join_script(i
);
8785 player
[i
].playkeys
= player
[i
].newkeys
= player
[i
].releasekeys
= 0;
8788 drop_all_enemies(); //27-12-2004 If drop enemies is on, drop all enemies
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
) {
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
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
) {
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
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
);
8872 font_printf(videomodes
.shiftpos
[i
] + pnameJ
[i
][4], savedata
.windowpos
+ pnameJ
[i
][5],
8873 pnameJ
[i
][6], 0, "Please Wait");
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;
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;
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
;
8904 --player
[i
].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;
8914 font_printf(videomodes
.shiftpos
[i
] + pnameJ
[i
][4], savedata
.windowpos
+ pnameJ
[i
][5],
8915 pnameJ
[i
][6], 0, "GAME OVER");
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
;
8935 if(dt
< oldtime
|| oldtime
== 0) {
8936 execute_timetick_script(dt
, go_time
);
8941 spriteq_add_sprite(videomodes
.hShift
+ timeicon_offsets
[0], savedata
.windowpos
+ timeicon_offsets
[1],
8942 10000, timeicon
, NULL
, 0);
8944 font_printf(videomodes
.hShift
+ timeloc
[0] + 2, savedata
.windowpos
+ timeloc
[1] + 2, timeloc
[5], 0,
8947 font_printf(videomodes
.hShift
+ 113, videomodes
.vShift
+ savedata
.windowpos
+ 110, timeloc
[5], 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
8960 spriteq_add_sprite(40, 60 + videomodes
.vShift
, 10000, golsprite
, NULL
, 0); // new sprite for left direction
8962 drawmethod
.table
= 0;
8963 drawmethod
.flipx
= 1;
8964 spriteq_add_sprite(40, 60 + videomodes
.vShift
, 10000, gosprite
, &drawmethod
, 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
,
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
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
;
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
;
9006 icon
= ent
->modeldata
.icon
;
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
));
9024 for(i
= 0; i
< MAX_PLAYERS
; i
++) {
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
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
);
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
,
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
,
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();
9086 if(ticks
- keybtick
> 250) {
9087 control_update(playercontrolpointers
, 1); // Respond to exit and/or fullscreen requests from user/OS
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
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
)) {
9104 putscreen(vscreen
, background
, 0, 0, NULL
);
9106 clearscreen(vscreen
);
9108 spriteq_draw(vscreen
, 0);
9109 video_copy_screen(vscreen
);
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
);
9114 font_printf(120 + videomodes
.hShift
, 110 + videomodes
.vShift
, 0, 0, "Loading...");
9115 spriteq_draw(vscreen
, 0);
9116 video_copy_screen(vscreen
);
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
;
9129 return; //dont score if <0, e.g., npc damage enemy, enemy damage enemy
9133 next1up
= ((s
/ lifescore
) + 1) * lifescore
;
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
) {
9182 clear_all_scripts(&e
->scripts
, 2);
9183 free_all_scripts(&e
->scripts
);
9185 freeEntityFactors(e
);
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
);
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
));
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
);
9228 for(i
= 0; i
< MAX_ENTS
; i
++) {
9229 ent_list
[i
] = alloc_ent();
9234 ent_list
[i
]->sortid
= i
* 100;
9236 ent_count
= ent_max
= 0;
9240 // this method initialize an entity's A.I. behaviors
9241 void ent_default_init(entity
* e
) {
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
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);
9260 } else if(selectScreen
&& validanim(e
, ANI_SELECT
))
9261 ent_set_anim(e
, ANI_SELECT
, 0);
9270 switch (e
->modeldata
.type
) {
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
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
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;
9305 e
->think
= common_think
;
9306 if(e
->modeldata
.subtype
== SUBTYPE_BIKER
) {
9309 //e->direction = (e->x<0);
9310 if(e
->modeldata
.speed
)
9311 e
->xdir
= (e
->direction
) ? (e
->modeldata
.speed
) : (-e
->modeldata
.speed
);
9314 (e
->direction
) ? (1.7 + randf((float) 0.6)) : (-(1.7 + randf((float) 0.6)));
9315 e
->takedamage
= biker_takedamage
;
9318 // define new subtypes
9319 else if(e
->modeldata
.subtype
== SUBTYPE_ARROW
) {
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;
9331 e
->takedamage
= arrow_takedamage
;
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
)
9348 if(validanim(e
, ANI_SPAWN
) /*|| validanim(e,ANI_RESPAWN) */ ) {
9349 e
->takeaction
= common_spawn
;
9351 dodrop
= (e
->modeldata
.subtype
!= SUBTYPE_ARROW
&& level
9352 && (level
->scrolldir
== SCROLL_UP
|| level
->scrolldir
== SCROLL_DOWN
));
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);
9365 e
->think
= trap_think
;
9366 e
->takedamage
= common_takedamage
; //enemy_takedamage;
9371 e
->dead
= 1; // so it won't get hit
9372 e
->takedamage
= obstacle_takedamage
; //obstacle_takedamage;
9376 e
->think
= steamer_think
;
9379 case TYPE_TEXTBOX
: // New type for displaying text purposes
9381 e
->think
= text_think
;
9386 e
->think
= common_think
;
9387 e
->takedamage
= arrow_takedamage
;
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;
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
)) {
9408 e
->xdir
= e
->modeldata
.speed
;
9410 e
->xdir
= -(e
->modeldata
.speed
);
9411 e
->think
= anything_walk
;
9413 common_walk_anim(e
);
9414 //ent_set_anim(e, ANI_WALK, 0);
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)
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
))
9446 spawn(advancex
+ spawnframe
[1], advancey
+ spawnframe
[2], spawnframe
[3], 0, NULL
,
9447 ent
->animation
->subentity
, NULL
);
9450 spawn(advancex
+ spawnframe
[1], spawnframe
[2], spawnframe
[3], 0, NULL
,
9451 ent
->animation
->subentity
, NULL
);
9453 //absolute position in level
9456 spawn(spawnframe
[1], spawnframe
[2], spawnframe
[3] + 0.001, 0, NULL
, ent
->animation
->subentity
,
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
)
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)
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
))
9482 spawn(advancex
+ spawnframe
[1], advancey
+ spawnframe
[2], spawnframe
[3], 0, NULL
,
9483 ent
->animation
->subentity
, NULL
);
9486 spawn(advancex
+ spawnframe
[1], spawnframe
[2], spawnframe
[3], 0, NULL
,
9487 ent
->animation
->subentity
, NULL
);
9489 //absolute position in level
9491 s_ent
= spawn(spawnframe
[1], spawnframe
[2], spawnframe
[3], 0, NULL
, ent
->animation
->subentity
, NULL
);
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
)
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
) {
9513 float move
, movez
, movea
;
9514 int iDelay
, iED_Mode
, iED_Capmin
, iED_CapMax
, iED_RangeMin
, iED_RangeMax
;
9517 if(f
>= ent
->animation
->numframes
) // prevent a crash with invalid frame index.
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?
9540 iDelay
= (int) (iDelay
* fED_Factor
);
9543 iDelay
+= (int) fED_Factor
;
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)
9566 self
->trymove(move
, 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
;
9580 self
->altbase
+= movea
;
9585 if(self
->animation
->flipframe
== f
)
9586 self
->direction
= !self
->direction
;
9588 if(self
->animation
->weaponframe
&& self
->animation
->weaponframe
[0] == f
) {
9590 set_weapon(self
, self
->animation
->weaponframe
[1], 0);
9594 if(self
->animation
->quakeframe
[0] + self
->animation
->quakeframe
[3] == f
) {
9596 if(self
->animation
->quakeframe
[3] % 2 || self
->animation
->quakeframe
[2] > 0)
9597 level
->quake
= self
->animation
->quakeframe
[2];
9599 level
->quake
= self
->animation
->quakeframe
[2] * -1;
9601 if((self
->animation
->quakeframe
[1] - self
->animation
->quakeframe
[3]) > 1)
9602 self
->animation
->quakeframe
[3]++;
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
) {
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
);
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
;
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)
9661 else if(self
->animation
->custstar
>= 0)
9663 else if(self
->jumping
) {
9666 } else if(!__tryknife
)
9671 if(self
->animation
->shootframe
== f
) {
9672 knife_spawn(NULL
, -1, self
->x
, self
->z
, self
->a
, self
->direction
, 1, 0);
9676 if(self
->animation
->tossframe
== f
) {
9677 bomb_spawn(NULL
, -1, self
->x
, self
->z
, self
->a
+ self
->animation
->throwa
, self
->direction
, 0);
9685 void ent_set_anim(entity
* ent
, int aninum
, int resetable
) {
9689 printf("FATAL: tried to set animation with invalid address (no such object)");
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
);
9698 if(!validanim(ent
, aninum
)) {
9699 printf("FATAL: tried to set animation with invalid address (%s, %i)", ent
->name
, aninum
);
9703 ani
= ent
->modeldata
.animation
[aninum
];
9705 if(!resetable
&& ent
->animation
== ani
)
9708 if(ani
->numframes
== 0)
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;
9718 ent
->lastanimpos
= -1;
9720 ent
->lasthit
= ent
->grabbing
;
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
)
9733 ent
->colourmap
= NULL
;
9735 ent
->colourmap
= ent
->modeldata
.colourmap
[which
- 1];
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
) {
9774 shutdown(1, "FATAL: tried to change model of invalid object");
9775 m
= findmodel(modelname
);
9777 shutdown(1, "Model not found: '%s'", modelname
);
9778 oldmodel
= ent
->modeldata
;
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);
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);
9796 ent_set_anim(ent
, ANI_IDLE
, 0);
9797 } else if(selectScreen
&& validanim(ent
, ANI_SELECT
))
9798 ent_set_anim(ent
, ANI_SELECT
, 0);
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
) {
9807 float *dfs
, *dfsp
, *dfsk
, *dfsbp
, *dfsbt
, *dfsbr
, *dfsbe
, *ofs
;
9808 ScriptVariant
*vars
;
9809 s_scripts scripts_save
;
9813 model
= model_cache
[index
].model
;
9815 model
= findmodel(name
);
9817 // Be a bit more tolerant...
9820 printf("FATAL: attempt to spawn object with invalid model cache id (%d)!\n", index
);
9822 printf("FATAL: attempt to spawn object with invalid model name (%s)!\n", name
);
9826 for(i
= 0; i
< MAX_ENTS
; i
++) {
9827 if(!ent_list
[i
]->exists
) {
9829 // save these values, or they will loss when memset called
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
;
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
);
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
9859 e
->modeldata
= *model
; // copy the entir model data here
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
;
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
)
9890 strncpy(e
->name
, e
->modeldata
.name
, MAX_NAME_LEN
);
9891 // copy back the value
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
;
9903 ent_default_init(e
);
9912 // Break the link an entity has with another one
9913 void ent_unlink(entity
* e
) {
9915 e
->link
->link
= NULL
;
9916 e
->link
->grabbing
= NULL
;
9924 // Link two entities together
9925 void ents_link(entity
* e1
, entity
* e2
) {
9928 e1
->grabbing
= e2
; // Added for platform layering
9935 void kill(entity
* victim
) {
9938 entity
*tempent
= self
;
9940 execute_onkill_script(victim
);
9942 if(!victim
|| !victim
->exists
)
9945 if(victim
->modeldata
.type
== TYPE_SHOT
&& player
[(int) victim
->playerindex
].ent
)
9946 player
[(int) victim
->playerindex
].ent
->cantfire
= 0;
9949 victim
->weapent
= NULL
;
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;
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
);
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
)
9990 if(victim
== textbox
)
9993 for(i
= 0; i
< ent_max
; i
++) {
9994 if(ent_list
[i
]->exists
) {
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
);
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
)
10029 for(i
= 0; i
< ent_max
; i
++) {
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;
10041 int checkhit(entity
* attacker
, entity
* target
, int counter
) {
10044 int x1
, x2
, y1
, y2
;
10046 int debug_coords
[2][4];
10047 int topleast
, bottomleast
, leftleast
, rightleast
;
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
))
10056 coords1
= attacker
->animation
->attacks
[attacker
->animpos
]->attack_coords
;
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
;
10068 zdist
+= coords1
[4];
10070 zdist
+= attacker
->modeldata
.grabdistance
/ 3;
10072 zdist
+= coords2
[4];
10074 if(diff(attacker
->z
, target
->z
) > zdist
)
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];
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];
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])
10108 if(debug_coords
[1][0] > debug_coords
[0][2])
10110 if(debug_coords
[0][1] > debug_coords
[1][3])
10112 if(debug_coords
[1][1] > debug_coords
[0][3])
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
10127 if(attacker
->z
> target
->z
)
10128 lasthitz
= attacker
->z
+ 1; // Changed so flashes always spawn in front
10130 lasthitz
= target
->z
+ 1;
10132 lasthita
= lasthitz
- medy
;
10133 lasthitt
= attacker
->animation
->attacks
[attacker
->animpos
]->attack_type
;
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
)
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
) {
10167 for(i
= 0, c
= 0; i
< level
->numholes
; i
++)
10168 c
+= (level
->holesfound
[i
] = testhole(i
, x
, z
));
10173 // find the 1st hole here
10174 int checkhole(float x
, float z
) {
10180 if(level
->exit_hole
) {
10181 if(x
> level
->width
- (PLAYER_MAX_Z
- z
))
10185 for(i
= 0; i
< level
->numholes
; i
++) {
10186 if(testhole(i
, x
, z
)) {
10196 // find all walls here within altitude1 and 2, return the count
10197 int checkwalls(float x
, float z
, float a1
, float a2
) {
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
));
10207 // get a highest wall below this altitude
10208 int checkwall_below(float x
, float z
, float a
) {
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
;
10227 // return the 1st wall found here
10228 int checkwall(float x
, float z
) {
10233 for(i
= 0; i
< level
->numwalls
; i
++)
10234 if(testwall(i
, x
, z
))
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
;
10249 if(!plat
->animation
|| !plat
->animation
->platform
|| !plat
->animation
->platform
[plat
->animpos
][7])
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]);
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
)
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
;
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
) {
10289 //find a lowest platform above this altitude
10290 entity
*check_platform_above(float x
, float z
, float a
) {
10292 entity
*plat
= NULL
;
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
) {
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
) {
10315 entity
*plat
= NULL
;
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];
10333 return (ind
>= 0) ? ent_list
[ind
] : NULL
;
10336 // find the 1st platform entity here
10337 entity
*check_platform(float x
, float z
) {
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
];
10350 // find real opponent
10351 void set_opponent(entity
* ent
, entity
* other
) {
10352 entity
*realself
, *realother
;
10358 while(realself
->owner
)
10359 realself
= realself
->owner
;
10362 while(realother
&& realother
->owner
)
10363 realother
= realother
->owner
;
10365 realself
->opponent
= ent
->opponent
= realother
;
10367 realother
->opponent
= other
->opponent
= realself
;
10372 void do_attack(entity
* e
) {
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
;
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;
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
)
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
;
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
) {
10410 if(new_attack_id
== 0)
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
++) {
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
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
);
10447 } //12312010, DC: Allows modder to cancel engine's attack handling. Useful for parry systems, alternate blocking, or other scripted hit events.
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
)
10459 //if #02 , ground missle checking, and bullets wont hit each other
10460 if((e
->owner
&& self
->owner
) || (e
->modeldata
.ground
&& inair(e
))) {
10464 //if #05, blocking code section
10466 if(attack
->attack_type
== ATK_ITEM
) {
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
,
10475 if(self
->toexplode
== 1)
10476 self
->toexplode
= 2; // Used so the bomb type entity explodes when hit
10478 if(e
->toexplode
== 1)
10479 e
->toexplode
= 2; // Used so the bomb type entity explodes when hitting
10482 self
->modeldata
.jugglepoints
[0] = self
->modeldata
.jugglepoints
[0] - attack
->jugglecost
; //reduce available juggle points.
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
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
10516 flash
= spawn(lasthitx
, lasthitz
, lasthita
, 0, NULL
, ent_list
[i
]->modeldata
.bflash
, NULL
); // New block flash that can be smaller
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
10524 execute_onspawn_script(flash
);
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
);
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
10558 flash
= spawn(lasthitx
, lasthitz
, lasthita
, 0, NULL
, ent_list
[i
]->modeldata
.bflash
, NULL
); // New block flash that can be smaller
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
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
10593 flash
= spawn(lasthitx
, lasthitz
, lasthita
, 0, NULL
, ent_list
[i
]->modeldata
.bflash
, NULL
); // New block flash that can be smaller
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
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
;
10612 if(!attack
->no_flash
) {
10613 if(!self
->modeldata
.noatflash
) {
10614 if(attack
->hitflash
>= 0)
10616 spawn(lasthitx
, lasthitz
, lasthita
, 0, NULL
,
10617 attack
->hitflash
, NULL
);
10620 spawn(lasthitx
, lasthitz
, lasthita
, 0, NULL
,
10621 e
->modeldata
.flash
, NULL
);
10624 spawn(lasthitx
, lasthitz
, lasthita
, 0, NULL
,
10625 self
->modeldata
.flash
, NULL
);
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
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;
10659 // 2007 3 24, hmm, def should be like this
10660 if(didblock
&& !def
)
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
10679 self
->hit_by_attack_id
= current_attack_id
;
10681 self
->blocking
= didblock
; // yeah, if get hit, stop blocking
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)
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.
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?
10717 def
->modeldata
.defense_blockratio
[(short) attack
->attack_type
]);
10718 } else { //No typed. Use static block ratio.
10722 if(mpblock
&& !def
->modeldata
.defense_blocktype
[(short) attack
->attack_type
]) { // Drain MP bar first?
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.
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.
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?
10749 self
->takedamage(e
, attack
); // Must be a fatal attack, then!
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];
10763 if(attack
->blocksound
>= 0)
10764 sound_play_sample(attack
->blocksound
, 0, savedata
.effectvol
, savedata
.effectvol
, 100); // New custom block sound effect
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);
10771 if(attack
->hitsound
>= 0)
10772 sound_play_sample(attack
->hitsound
, 0, savedata
.effectvol
, savedata
.effectvol
,
10775 sound_play_sample(samples
.beat
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
10777 if(attack
->hitsound
>= 0)
10778 sound_play_sample(attack
->hitsound
, 0, savedata
.effectvol
, savedata
.effectvol
,
10781 sound_play_sample(samples
.beat
, 0, savedata
.effectvol
, savedata
.effectvol
,
10786 if(e
->remove_on_attack
)
10792 void check_gravity() {
10794 entity
*other
, *dust
;
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
);
10806 if(self
->animation
->height
)
10807 heightvar
= self
->animation
->height
;
10809 heightvar
= self
->modeldata
.height
;
10811 if(other
&& other
->a
<= self
->a
+ heightvar
) {
10812 if(self
->hithead
== NULL
) // bang! Hit the ceiling.
10815 self
->hithead
= other
;
10816 execute_onblocka_script(self
, other
);
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
);
10835 execute_onmovea_script(self
); //Move A event.
10837 if(self
->idling
&& validanim(self
, ANI_WALKOFF
)) {
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
;
10847 //self->projectile = 0;
10848 // cust dust entity
10849 if(self
->modeldata
.dust
[0] >= 0 && self
->tossv
< -1 && self
->drop
) {
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
);
10858 if(tobounce(self
) && self
->modeldata
.bounce
) {
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;
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) {
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
);
10900 self
->health
-= self
->damage_on_landing
;
10901 if(self
->health
<= 0)
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
10915 void check_lost() {
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
)
10932 // fall in to a pit
10933 if(self
->a
< PIT_DEPTH
|| self
->lifespancountdown
< 0) {
10934 if(!self
->takedamage
)
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
);
10948 if(!is_frozen(self
) && self
->lifespancountdown
!= (float) 0xFFFFFFFF)
10949 self
->lifespancountdown
--;
10953 void check_link_move(float xdir
, float zdir
) {
10954 float x
, z
, gx
, gz
;
10956 entity
*tempself
= self
;
10957 gx
= self
->grabbing
->x
;
10958 gz
= self
->grabbing
->z
;
10961 self
= self
->grabbing
;
10962 tryresult
= self
->trymove(xdir
, zdir
);
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
;
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
) {
10985 if(self
->grabbing
&& self
->grabwalking
&& self
->grabbing
->trymove
) {
10986 check_link_move(plat
->xdir
, plat
->zdir
);
10988 self
->trymove(plat
->xdir
, plat
->zdir
);
10990 self
->x
+= plat
->xdir
;
10991 self
->z
+= plat
->zdir
;
10995 if(self
->nextthink
<= borTime
&& !endgame
) {
10996 self
->update_mark
|= 2; //mark it
10998 if(self
->takeaction
)
10999 self
->takeaction();
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
)
11009 // Execute think script
11010 execute_think_script(self
);
11013 if(self
->xdir
|| self
->zdir
) {
11014 if(self
->trymove
) {
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
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) */ )
11036 void update_animation() {
11038 float move
, movez
, seta
;
11039 entity
*other
= NULL
;
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
));
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
));
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;
11076 self
->invinctime
= 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
);
11087 self
->colourmap
= self
->modeldata
.colourmap
[self
->dying
- 1];
11092 if(self
->freezetime
&& borTime
>= self
->freezetime
) {
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.
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]) {
11114 f
= self
->animation
->numframes
- 1;
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
) {
11129 } else if((unsigned) f
>= (unsigned) self
->animation
->numframes
) {
11131 f
= self
->animation
->numframes
- 1;
11135 if(!self
->animation
->loop
[0]) {
11136 self
->animating
= 0;
11138 if(self
->autokill
) {
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;
11163 other
= check_platform_below(self
->x
, self
->z
, self
->a
+ 1);
11166 self
->landed_on_platform
= other
;
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
);
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;
11185 } else if(!hole
&& self
->base
== -1000) {
11189 self
->xdir
= self
->zdir
= 0; // hit the hole border
11194 if(self
->base
!= -1000 || wall
>= 0) {
11195 if(other
!= NULL
&& other
!= self
) {
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
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
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
;
11225 f
= other
->animpos
+ other
->animating
;
11226 if(f
>= other
->animation
->numframes
) {
11228 f
= other
->animation
->numframes
- 1;
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)
11237 if(move
|| movez
) {
11238 if(self
->trymove
) {
11239 self
->trymove(move
, movez
);
11249 void check_attack() {
11251 if(self
->falling
&& !self
->projectile
) {
11252 self
->attack_id
= 0;
11256 if(self
->drop
&& !self
->falling
) {
11257 self
->attack_id
= 0;
11260 // Can't hit an opponent if you are frozen
11261 if(!is_frozen(self
) && self
->animation
->attacks
&& self
->animation
->attacks
[self
->animpos
]) {
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];
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
) {
11300 if(self
->mp
< self
->modeldata
.mpstableval
)
11301 self
->mp
+= self
->modeldata
.mprate
;
11304 if(self
->mp
> self
->modeldata
.mpstableval
)
11305 self
->mp
-= self
->modeldata
.mpdroprate
;
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
;
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
;
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
)
11342 else if(self
->oldhealth
> self
->health
)
11345 if(self
->oldmp
< self
->mp
)
11347 else if(self
->oldmp
> self
->mp
)
11351 void common_dot() {
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.
11402 iFForce
= (int) (iForce
* fOffense
);
11403 } //Apply offense factors.
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.
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.
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.
11439 self
->mp
= 0; //Stablize MP at 0.
11446 void adjust_bind(entity
* e
) {
11448 if(e
->animnum
!= e
->bound
->animnum
) {
11449 if(!validanim(e
, e
->bound
->animnum
)) {
11450 if(e
->bindanim
& 4) {
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]) {
11466 if(e
->bound
->direction
)
11467 e
->x
= e
->bound
->x
+ e
->bindoffset
[0];
11469 e
->x
= e
->bound
->x
- e
->bindoffset
[0];
11472 e
->direction
= e
->bound
->direction
;
11473 if(e
->bound
->direction
)
11474 e
->x
= e
->bound
->x
+ e
->bindoffset
[0];
11476 e
->x
= e
->bound
->x
- e
->bindoffset
[0];
11479 e
->direction
= !e
->bound
->direction
;
11480 if(e
->bound
->direction
)
11481 e
->x
= e
->bound
->x
+ e
->bindoffset
[0];
11483 e
->x
= e
->bound
->x
- e
->bindoffset
[0];
11487 e
->x
= e
->bound
->x
+ e
->bindoffset
[0];
11491 e
->x
= e
->bound
->x
+ e
->bindoffset
[0];
11494 e
->x
= e
->bound
->x
+ e
->bindoffset
[0];
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() {
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
]);
11514 for(i
= 0; i
< ent_max
; i
++) {
11515 if(!ent_list
[i
]->exists
&& ind
< 0)
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
;
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() {
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;
11542 check_lost(); // check lost caused by level scrolling or lifespan
11545 // expand time incase being frozen
11546 if(is_frozen(self
)) {
11549 execute_updateentity_script(self
); // execute a script
11552 check_ai(); // check ai
11555 check_gravity(); // check gravity
11558 update_animation(); // if not frozen, update animation
11561 check_attack(); // Collission detection
11564 update_health(); // Update displayed health
11572 void display_ents() {
11574 int i
, z
, wall
= 0, wall2
;
11576 entity
*other
= NULL
;
11577 int qx
, qy
, sy
, sz
, alty
;
11578 float temp1
, temp2
;
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
) {
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
)
11618 if(other
&& other
!= e
11619 && e
->a
>= other
->a
+ other
->animation
->platform
[other
->animpos
][7]) {
11621 && ((e
->modeldata
.grabback
&& !e
->grabbing
)
11622 || (e
->link
->modeldata
.grabback
&& e
->link
->grabbing
)
11625 z
= (int) (other
->z
+ 2); // Make sure entities get displayed in front of obstacle and grabbee
11628 z
= (int) (other
->z
+ 1); // Entity should always display in front of the obstacle
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
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
);
11647 commonmethod
= plainmethod
;
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];
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];
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
);
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
-
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
);
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 */ );
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) {
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
;
11740 sz
= PANEL_Z
- HUD_Z
;
11741 if(e
->animation
->shadow_coords
) {
11744 e
->animation
->shadow_coords
[e
->animpos
][0];
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);
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
11779 e
->animation
->shadow
? e
->animation
->shadow
[e
->animpos
] : e
->modeldata
.
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
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] +
11794 sy
= (int) ((2 * MIRROR_Z
- e
->z
) - other
->a
-
11795 other
->animation
->platform
[other
->animpos
][7] +
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
+
11806 sz
= PANEL_Z
- HUD_Z
;
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
);
11812 sz
= PANEL_Z
- HUD_Z
;
11814 if(e
->animation
->shadow_coords
) {
11816 qx
+= e
->animation
->shadow_coords
[e
->animpos
][0];
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],
11828 spriteq_add_sprite(qx
, sy
, sz
, shadowsprites
[useshadow
- 1],
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)
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)
11851 void toss(entity
* ent
, float lift
) {
11854 ent
->toss_time
= borTime
+ 1;
11856 ent
->a
+= 0.5; // Get some altitude (needed for checks)
11861 entity
*findent(int types
) {
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
];
11875 int count_ents(int types
) {
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
));
11888 entity
*find_ent_here(entity
* exclude
, float x
, float z
, int types
) {
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
];
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
)) {
11909 ent
->attacking
= 0;
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
);
11923 iDie
->charging
= 0;
11924 iDie
->attacking
= 0;
11925 iDie
->blocking
= 0;
11928 if(type
< 0 || type
>= dyn_anim_custom_maxvalues
.max_attack_types
|| !validanim(iDie
, dyn_anims
.animdies
[type
]))
11930 if(validanim(iDie
, dyn_anims
.animdies
[type
]))
11931 ent_set_anim(iDie
, dyn_anims
.animdies
[type
], reset
);
11938 iDie
->charging
= 0;
11939 iDie
->attacking
= 0;
11940 iDie
->blocking
= 0;
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
]))
11951 if(validanim(iFall
, dyn_anims
.animfalls
[type
]))
11952 ent_set_anim(iFall
, dyn_anims
.animfalls
[type
], reset
);
11958 iFall
->falling
= 1;
11959 iFall
->jumping
= 0;
11960 iFall
->getting
= 0;
11961 iFall
->charging
= 0;
11962 iFall
->attacking
= 0;
11963 iFall
->blocking
= 0;
11967 execute_onfall_script(iFall
, other
, force
, drop
, type
, noblock
, guardcost
, jugglecost
, pauseadd
);
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
]))
11975 if(validanim(iRise
, dyn_anims
.animrises
[type
]))
11976 ent_set_anim(iRise
, dyn_anims
.animrises
[type
], reset
);
11979 iRise
->takeaction
= common_rise
;
11982 iRise
->falling
= 0;
11983 iRise
->projectile
= 0;
11985 iRise
->xdir
= self
->zdir
= self
->tossv
= 0;
11986 iRise
->modeldata
.jugglepoints
[0] = iRise
->modeldata
.jugglepoints
[1]; //reset jugglepoints
11990 int set_riseattack(entity
* iRiseattack
, int type
, int reset
) {
11991 if(!validanim(iRiseattack
, dyn_anims
.animriseattacks
[type
]) && iRiseattack
->modeldata
.riseattacktype
== 1)
11993 if(iRiseattack
->modeldata
.riseattacktype
== 0 || type
< 0 || type
>= dyn_anim_custom_maxvalues
.max_attack_types
)
11995 if(validanim(iRiseattack
, dyn_anims
.animriseattacks
[type
]))
11996 ent_set_anim(iRiseattack
, dyn_anims
.animriseattacks
[type
], reset
);
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
12009 int set_blockpain(entity
* iBlkpain
, int type
, int reset
) {
12010 if(!validanim(iBlkpain
, ANI_BLOCKPAIN
)) {
12011 iBlkpain
->takeaction
= common_pain
;
12014 if(type
< 0 || type
>= dyn_anim_custom_maxvalues
.max_attack_types
|| !validanim(iBlkpain
, dyn_anims
.animblkpains
[type
]))
12016 if(validanim(iBlkpain
, dyn_anims
.animblkpains
[type
]))
12017 ent_set_anim(iBlkpain
, dyn_anims
.animblkpains
[type
], reset
);
12020 iBlkpain
->takeaction
= common_block
;
12021 set_attacking(iBlkpain
);
12025 int set_pain(entity
* iPain
, int type
, int reset
) {
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
;
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
);
12044 if(pain
== ANI_GRABBED
)
12050 iPain
->falling
= 0;
12051 iPain
->projectile
= 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];
12063 execute_onpain_script(iPain
, type
, reset
);
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
;
12071 int animnum
, animpos
;
12073 int type
= ent
->modeldata
.type
;
12075 model
= ent
->model
;
12078 newmodel
= model_cache
[index
].model
;
12080 newmodel
= findmodel(modelname
);
12083 shutdown(1, "Can't set model for entity '%s', model not found.\n", ent
->name
);
12084 if(newmodel
== model
)
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
;
12128 ent
->attacking
= 0;
12129 ent_set_model(ent
, newmodel
->name
);
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
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
);
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
;
12161 player
[(int) ent
->playerindex
].weapnum
= level
->setweap
;
12165 //////////////////////////////////////////////////////////////////////////
12166 // common A.I. code for enemies & NPCs
12167 //////////////////////////////////////////////////////////////////////////
12170 entity
*melee_find_target() {
12174 entity
*long_find_target() {
12178 entity
*normal_find_target(int anim
) {
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).
12193 (!ent_list
[index
]->animation
->vulnerable
[ent_list
[index
]->animpos
] ||
12194 ent_list
[index
]->invincible
== 1
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
)
12206 return ent_list
[index
];
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() {
12238 //find the 'nearest' one
12239 for(i
= 0; i
< ent_max
; 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
))
12253 || diff(ce
->x
, self
->x
) + diff(ce
->z
, self
->z
) < diff(ent_list
[index
]->x
,
12254 self
->x
) + diff(ent_list
[index
]->z
,
12260 return ent_list
[index
];
12264 int long_attack() {
12268 int melee_attack() {
12272 // chose next attack in atchain, if succeeded, return 1, otherwise return 0.
12273 int perform_atchain() {
12275 if(self
->combotime
> borTime
)
12276 self
->combostep
[0]++;
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;
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
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]))) {
12306 self
->combostep
[0] = 0;
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
;
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
12328 self
->takeaction
= NULL
;
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
;
12338 ent_set_anim(self
, ANI_TURN
, 0);
12339 self
->takeaction
= common_turn
;
12343 if(borTime
< self
->stalltime
)
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
;
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)) {
12366 if(self
->modeldata
.chainlength
> 1) // have a chain?
12368 if(perform_atchain())
12370 } else if(target
) // dont have a chain so just select an attack randomly
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)
12381 if(lastpick
>= 0) {
12382 set_attacking(self
);
12383 ent_set_anim(self
, lastpick
, 0);
12384 self
->takeaction
= common_attack_proc
;
12388 // No attack to perform, return to A.I. root
12390 self
->takeaction
= NULL
;
12393 void common_jumpland() {
12394 if(self
->animating
)
12397 self
->takeaction
= NULL
;
12400 //A.I characters play the jump animation
12401 void common_jump() {
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];
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
12422 if(self
->tossv
<= 0) // wait if it is still go up
12425 self
->a
= self
->base
;
12428 self
->attacking
= 0;
12430 if(!self
->modeldata
.runhold
)
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) {
12440 spawn(self
->x
, self
->z
, self
->a
, self
->direction
, NULL
, self
->modeldata
.dust
[1],
12442 dust
->base
= self
->a
;
12443 dust
->autokill
= 1;
12444 execute_onspawn_script(dust
);
12446 self
->takeaction
= common_jumpland
;
12448 if(self
->modeldata
.dust
[1] >= 0 && self
->animation
->landframe
[0] == -1) {
12450 spawn(self
->x
, self
->z
, self
->a
, self
->direction
, NULL
, self
->modeldata
.dust
[1],
12452 dust
->base
= self
->a
;
12453 dust
->autokill
= 1;
12454 execute_onspawn_script(dust
);
12456 if(self
->animation
->landframe
[0] >= 0 && self
->animating
)
12460 self
->takeaction
= NULL
; // back to A.I. root
12465 //A.I. characters spawn
12466 void common_spawn(void) {
12468 if(self
->animating
)
12471 self
->takeaction
= NULL
; // come to life
12474 //A.I. characters drop from the sky
12475 void common_drop(void) {
12479 self
->takeaction
= NULL
;
12480 if(self
->health
<= 0)
12484 //walk off a wall/cliff
12485 void common_walkoff(void) {
12486 if(inair(self
) || self
->animating
)
12488 self
->takeaction
= NULL
;
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
;
12498 self
->takeaction
= NULL
;
12502 // switch to land animation, land safely
12503 void doland(void) {
12504 self
->xdir
= self
->zdir
= 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
;
12514 self
->takeaction
= NULL
;
12518 void common_fall(void) {
12520 if(self
->falling
|| inair(self
) || self
->tossv
) {
12524 //self->xdir = self->zdir;
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
12535 //self->projectile = 0;
12538 // Drop Weapon due to Enemy Falling.
12539 if(self
->modeldata
.weaploss
[0] == 1)
12542 if(self
->boss
&& level_completed
)
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) {
12555 if(!validanim(self
, ANI_RISEATTACK
))
12558 target
= normal_find_target(ANI_RISEATTACK
);
12560 self
->direction
= !self
->direction
;
12561 target
= normal_find_target(ANI_RISEATTACK
);
12562 self
->direction
= !self
->direction
;
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) {
12573 if(self
->health
<= 0) {
12574 // Drop Weapon due to death.
12575 if(self
->modeldata
.weaploss
[0] <= 2)
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
;
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
;
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.
12612 if(borTime
< self
->stalltime
|| self
->a
!= self
->base
|| self
->tossv
)
12615 //self->takeaction = common_rise;
12618 //self->falling = 0;
12619 //self->projectile = 0;
12620 //self->xdir = self->zdir = self->tossv = 0;
12622 set_rise(self
, self
->damagetype
, 0);
12626 void common_rise(void) {
12627 if(self
->animating
)
12629 self
->staydown
[2] = 0; //Reset riseattack delay.
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
;
12640 void common_pain(void) {
12641 //self->xdir = self->zdir = 0; // complained
12643 if(self
->animating
|| inair(self
))
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
;
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);
12677 if(validanim(self
, grab_attacks
[which
][1]))
12678 ent_set_anim(self
, grab_attacks
[which
][1], 0);
12680 ent_set_anim(self
, ANI_ATTACK3
, 0);
12681 memset(self
->combostep
, 0, sizeof(int) * 5);
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);
12690 memset(self
->combostep
, 0, sizeof(int) * 5);
12692 self
->attacking
= 1;
12693 self
->takeaction
= common_grabattack
;
12696 void dovault(void) {
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
;
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) {
12716 entity
*other
= self
->link
;
12718 if(other
== NULL
|| (self
->modeldata
.grabfinish
&& self
->animating
&& !self
->grabwalking
))
12721 if(self
->base
!= other
->base
) { // Change this from ->a to ->base
12724 self
->takeaction
= NULL
;
12728 if(!nolost
&& self
->modeldata
.weaploss
[0] <= 0)
12731 self
->attacking
= 0; //for checking
12733 rnum
= rand32() & 31;
12735 if(borTime
> self
->releasetime
) {
12740 self
->takeaction
= NULL
;
12743 self
->releasetime
= borTime
+ (GAME_SPEED
/ 2);
12746 if(validanim(self
, ANI_THROW
) && rnum
< 7) {
12747 if(self
->modeldata
.throwframewait
>= 0)
12760 if(rnum
> 12 && validanim(self
, grab_attacks
[which
][0])) {
12761 dograbattack(which
);
12765 if(rnum
< 8 && validanim(self
, ANI_VAULT
)) {
12772 void common_grab(void) {
12773 // if(self->link) return;
12774 if(self
->link
|| (self
->modeldata
.grabfinish
&& self
->animating
&& !self
->grabwalking
))
12777 memset(self
->combostep
, 0, sizeof(int) * 5);
12779 self
->takeaction
= NULL
;
12780 self
->attacking
= 0;
12784 void common_grabbed(void) {
12785 // Just check if we're still grabbed...
12790 self
->stalltime
= 0;
12791 self
->takeaction
= NULL
;
12794 // picking up something
12795 void common_get(void) {
12796 if(self
->animating
)
12801 self
->takeaction
= NULL
;
12804 // A.I. characters do the block
12805 void common_block(void) {
12806 if(self
->animating
)
12810 self
->blocking
= 0;
12811 self
->takeaction
= NULL
;
12815 void common_charge(void) {
12816 if(self
->animating
)
12820 self
->charging
= 0;
12821 self
->takeaction
= NULL
;
12825 // common code for entities hold an item
12826 entity
*drop_item(entity
* e
) {
12829 memset(&p
, 0, sizeof(s_spawn_entry
));
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
);
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
)
12861 //drop the driver, just spawn, dont takedamage
12862 // damage will adjust by the biker
12863 entity
*drop_driver(entity
* e
) {
12867 memset(&p
, 0, sizeof(s_spawn_entry
));
12869 if(e
->modeldata
.rider
>= 0)
12870 p
.index
= e
->modeldata
.rider
;
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
;
12887 driver
= smartspawn(&p
);
12896 void checkdeath(void) {
12898 if(self
->health
> 0)
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
12908 sound_play_sample(self
->modeldata
.diesound
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
12911 if(self
->item
&& count_ents(TYPE_PLAYER
) > self
->itemplayer_count
) { //4player
12912 item
= drop_item(self
);
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
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
]))))
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
;
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
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.
12986 } else if(self
->frozen
) {
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)
13019 if(attack
->no_pain
)
13022 self
->projectile
= _blast
;
13025 self
->staydown
[0] = _staydown0
; //Staydown: Add to risetime until next rise.
13026 self
->staydown
[1] = _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
)
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
)
13068 if(magic_type
== 1) {
13069 if(other
->modeldata
.mprate
)
13070 other
->mp
+= other
->modeldata
.mprate
;
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
;
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
;
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
) {
13149 int common_takedamage(entity
* other
, s_attack
* attack
) {
13152 if(self
->toexplode
== 2)
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);
13162 set_opponent(self
, other
);
13164 if(attack
->attack_type
>= 0 && attack
->attack_type
< dyn_anim_custom_maxvalues
.max_attack_types
)
13165 self
->damagetype
= attack
->attack_type
;
13167 self
->damagetype
= ATK_NORMAL
;
13169 checkdamagedrop(attack
);
13170 // Drop Weapon due to being hit.
13171 if(self
->modeldata
.weaploss
[0] <= 0)
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.
13181 checkhitscore(other
, attack
);
13182 // check damage, cost hp.
13183 checkdamage(other
, attack
);
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
)
13196 // fall to the ground so dont fall again
13197 if(self
->damage_on_landing
) {
13198 self
->damage_on_landing
= 0;
13201 // unlink due to being hit
13202 if((self
->opponent
&& self
->opponent
->grabbing
!= self
) || self
->dead
|| self
->frozen
|| self
->drop
) {
13205 // Enemies can now use SPECIAL2 to escape cheap attack strings!
13206 if(self
->modeldata
.escapehits
) {
13208 self
->escapecount
= 0;
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)
13217 if(self
->health
<= 0 && self
->modeldata
.falldie
== 1) {
13218 self
->xdir
= self
->zdir
= self
->tossv
= 0;
13219 set_death(self
, self
->damagetype
, 0);
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)
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
)
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
;
13262 // A.I. try upper cut
13263 int common_try_upper(entity
* target
) {
13264 if(!validanim(self
, ANI_UPPER
))
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
;
13283 int common_try_runattack(entity
* target
) {
13284 if(!self
->running
|| !validanim(self
, ANI_RUNATTACK
))
13289 target
= normal_find_target(ANI_RUNATTACK
);
13292 self
->zdir
= self
->xdir
= 0;
13293 set_attacking(self
);
13294 ent_set_anim(self
, ANI_RUNATTACK
, 0);
13295 self
->takeaction
= common_attack_proc
;
13301 int common_try_block(entity
* target
) {
13302 if(self
->modeldata
.nopassiveblock
== 0 ||
13303 (rand32() & self
->modeldata
.blockodds
) != 1 || !validanim(self
, ANI_BLOCK
))
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
;
13321 int common_try_freespecial(entity* target)
13323 int i, s=dyn_anim_custom_maxvalues.max_freespecials;
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]))) &&
13332 check_costmove(animspecials[i], 1) )
13341 int common_try_normalattack(entity
* target
) {
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) {
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
]))
13358 if(target
|| (target
= normal_find_target(dyn_anims
.animattacks
[i
]))) {
13363 if(!found
&& validanim(self
, ANI_THROWATTACK
) &&
13364 self
->weapent
&& self
->weapent
->modeldata
.subtype
== SUBTYPE_PROJECTILE
&&
13365 (target
|| (target
= normal_find_target(ANI_THROWATTACK
)))) {
13370 self
->zdir
= self
->xdir
= 0;
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;
13379 borTime
+ (GAME_SPEED
/ 4) + (rand32() % (GAME_SPEED
/ 10) - self
->modeldata
.aggression
);
13386 int common_try_jumpattack(entity
* target
) {
13390 if((validanim(self
, ANI_JUMPATTACK
) || validanim(self
, ANI_JUMPATTACK2
))) {
13391 if(!validanim(self
, ANI_JUMPATTACK
))
13393 else if(validanim(self
, ANI_JUMPATTACK2
) && (rand32() & 1))
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;
13405 self
->xdir
= (float) -1.3;
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;
13418 set_attacking(self
);
13420 toss(self
, self
->modeldata
.jumpheight
);
13421 self
->takeaction
= common_jump
;
13423 if(self
->modeldata
.dust
[2] >= 0) {
13425 spawn(self
->x
, self
->z
, self
->a
, self
->direction
, NULL
, self
->modeldata
.dust
[2],
13427 dust
->base
= self
->a
;
13428 dust
->autokill
= 1;
13429 execute_onspawn_script(dust
);
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() {
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
)) {
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
13462 // we have done the throw, return to A.I. root
13463 self
->takeaction
= NULL
;
13466 // toss the grabbed one
13469 self
->xdir
= self
->zdir
= 0;
13470 other
= self
->link
;
13471 if(other
== NULL
) //change back to idle, or we will get stuck here
13474 self
->takeaction
= NULL
; // A.I. root again
13478 if(other
->modeldata
.throwheight
)
13479 toss(other
, other
->modeldata
.throwheight
);
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;
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);
13496 other
->takeaction
= common_fall
;
13497 self
->takeaction
= common_throw
;
13501 // Waiting until throw frame reached
13502 void common_throw_wait() {
13505 self
->takeaction
= NULL
; // A.I. root again
13509 self
->releasetime
+= THINK_SPEED
; //extend release time
13511 if(self
->animpos
!= self
->modeldata
.throwframewait
)
13518 void common_prethrow() {
13519 self
->running
= 0; // Quits running if grabbed by opponent
13521 // Just check if we're still grabbed...
13527 self
->takeaction
= NULL
; // A.I. root again
13530 // warp to its parent entity, just like skeletons in Diablo 2
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
;
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
;
13557 x2
= ent
->x
+ ((other
->x
> ent
->x
) ? dist
: -dist
);
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
))
13567 else if(other
->modeldata
.subject_to_screen
> 0 && (x2
< advancex
|| x2
> advancex
+ videomodes
.hRes
))
13570 wall1
= checkwall_below(x1
, z1
, 9999999);
13571 wall2
= checkwall_below(x2
, z2
, 9999999);
13572 if(wall1
< 0 && wall2
>= 0)
13574 else if(wall1
>= 0 && wall2
< 0)
13576 else if(wall1
>= 0 && level
->walls
[wall1
].alt
> a
)
13578 else if(wall2
>= 0 && level
->walls
[wall2
].alt
> a
)
13580 else if(wall1
>= 0 && wall2
>= 0 && level
->walls
[wall1
].alt
!= level
->walls
[wall2
].alt
)
13583 if(wall1
< 0 && checkhole(x1
, z1
))
13585 if(wall2
< 0 && checkhole(x2
, z2
))
13592 //other->a = ent->a;
13593 //other->base = ent->base;
13598 int common_trymove(float xdir
, float zdir
) {
13599 entity
*other
= NULL
;
13600 int wall
, heightvar
;
13601 float x
, z
, oxdir
, ozdir
;
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
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
;
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.
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
)) {
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
)) {
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])) {
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])) {
13693 execute_onblocko_script(self
, other
);
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
;
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
) {
13717 if(zdir
> 0 ? other
->z
> self
->z
: other
->z
< self
->z
) {
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
) {
13734 } else if(xdir
< -0.5) {
13737 if((wall
= checkwall(self
->x
+ xdir
, self
->z
)) >= 0 && level
->walls
[wall
].alt
> self
->a
) {
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
) {
13745 } else if(zdir
< -0.5) {
13748 if((wall
= checkwall(self
->x
, self
->z
+ zdir
)) >= 0 && level
->walls
[wall
].alt
> self
->a
) {
13750 execute_onblockw_script(self
, 2, (double) level
->walls
[wall
].alt
);
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;
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)
13790 // otherwise enemy_throw_wait will be used, kbandressen 10/20/06
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
;
13801 // --------------- end of grab/throw checking ------------------------
13803 // do move and return
13808 execute_onmovex_script(self
); //X move event.
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);
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;
13825 self
->xdir
= self
->modeldata
.speed
/ 2;
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;
13850 ent_set_anim(self
, ANI_DUCKATTACK
, 0);
13851 self
->takeaction
= common_attack_proc
;
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;
13862 ent_set_anim(self
, ANI_SLIDE
, 0);
13863 self
->takeaction
= common_attack_proc
;
13866 if(!check_platform_between(self
->x
, self
->z
, self
->a
, self
->a
+ self
->modeldata
.height
)) {
13868 self
->takeaction
= NULL
;
13874 // finish attacking, do something
13875 void common_attack_finish() {
13878 self
->xdir
= self
->zdir
= 0;
13880 if(self
->modeldata
.type
== TYPE_PLAYER
) {
13882 self
->takeaction
= NULL
;
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);
13892 self
->takeaction
= common_runoff
;
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)
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];
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) {
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() {
13934 if(self
->modeldata
.aiattack
== -1)
13937 aiattack
= self
->modeldata
.aiattack
& MASK_AIATTACK1
;
13939 switch (aiattack
) {
13940 case AIATTACK1_LONG
:
13941 case AIATTACK1_MELEE
:
13942 case AIATTACK1_NOATTACK
:
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() {
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
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
;
13967 xdir
= self
->x
- rmin
;
13969 if(self
->modeldata
.jumpmovez
)
13970 zdir
= self
->z
+ self
->zdir
;
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
) {
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
)) {
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.
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
;
14000 xdir
= self
->x
- rmin
;
14002 if(self
->modeldata
.jumpmovez
)
14003 zdir
= self
->z
+ self
->zdir
;
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.
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
);
14030 tryjump(self
->modeldata
.runjumpheight
,
14031 self
->modeldata
.jumpspeed
* self
->modeldata
.runjumpdist
,
14032 (self
->modeldata
.jumpmovez
) ? self
->zdir
: 0, ANI_JUMP
);
14034 if(validanim(self
, ANI_FORWARDJUMP
))
14035 tryjump(self
->modeldata
.jumpheight
, self
->modeldata
.jumpspeed
,
14036 (self
->modeldata
.jumpmovez
) ? self
->zdir
: 0, ANI_FORWARDJUMP
);
14038 tryjump(self
->modeldata
.jumpheight
, self
->modeldata
.jumpspeed
,
14039 (self
->modeldata
.jumpmovez
) ? self
->zdir
: 0, ANI_JUMP
);
14047 void adjust_walk_animation(entity
* other
) {
14048 if(self
->running
) {
14049 ent_set_anim(self
, ANI_RUN
, 0);
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);
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;
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
)
14080 self
->xdir
= self
->zdir
= 0;
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
;
14092 adjust_walk_animation(other
);
14097 void checkpathblocked() {
14098 if(self
->stalltime
>= borTime
) {
14099 if(self
->pathblocked
> 10) {
14100 self
->xdir
= self
->zdir
= 0;
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
14117 if(target
== NULL
|| self
->modeldata
.nomove
)
14120 dx
= diff(self
->x
, target
->x
);
14121 dz
= diff(self
->z
, target
->z
);
14123 aitype
= self
->modeldata
.aimove
& rand32();
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
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
)
14147 && validanim(self
, ANI_RUN
)))) {
14148 maxspeed
= self
->modeldata
.runspeed
;
14151 maxspeed
= self
->modeldata
.speed
;
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)
14162 (target
->z
> self
->z
) ? self
->modeldata
.speed
/ 2 : -self
->modeldata
.speed
/ 2;
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
;
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
);
14177 self
->xdir
= maxspeed
;
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
);
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
)
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
)
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
;
14215 maxspeed
= self
->modeldata
.speed
;
14220 self
->xdir
= self
->zdir
= 0;
14222 if(self
->modeldata
.runupdown
|| !self
->running
) {
14223 self
->xdir
= maxspeed
* dx
/ (dx
+ dz
);
14224 self
->zdir
= maxspeed
* dz
/ (dx
+ dz
);
14226 self
->xdir
= maxspeed
;
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
);
14241 // try to avoid the target
14242 // used by 'avoid avoidz avoidx
14243 int common_try_avoid(entity
* target
) {
14244 float maxspeed
= 0;
14249 if(target
== NULL
|| self
->modeldata
.nomove
)
14252 dx
= diff(self
->x
, target
->x
);
14253 dz
= diff(self
->z
, target
->z
);
14255 aitype
= self
->modeldata
.aimove
& rand32();
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
;
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;
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;
14288 self
->zdir
= (self
->z
< target
->z
) ? (-maxspeed
/ 2) : (maxspeed
/ 2);
14290 adjust_walk_animation(target
);
14295 // wander completely
14296 int common_try_wandercompletely() {
14300 if(self
->modeldata
.nomove
)
14305 self
->xdir
= self
->zdir
= 0;
14306 if((rnum
& 15) < 4) {
14308 self
->zdir
= -self
->modeldata
.speed
/ 2;
14309 } else if((rnum
& 15) > 11) {
14311 self
->zdir
= self
->modeldata
.speed
/ 2;
14315 if((rnum
& 15) < 4) {
14317 if(self
->direction
== 1)
14318 self
->xdir
= self
->modeldata
.speed
;
14320 self
->xdir
= -self
->modeldata
.speed
;
14321 } else if((rnum
& 15) > 11) {
14322 if(self
->modeldata
.noflip
) {
14324 if(self
->direction
== 1)
14325 self
->xdir
= -self
->modeldata
.speed
;
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
;
14335 self
->xdir
= -self
->modeldata
.speed
;
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;
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
))
14368 adjust_walk_animation(NULL
);
14375 // just wander around, face to the target
14376 int common_try_wander(entity
* target
) {
14378 int rnum
= rand32() & 7;
14380 if(target
== NULL
|| self
->modeldata
.nomove
)
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
;;
14389 } else if(rnum
< 3) {
14390 self
->xdir
= self
->modeldata
.speed
;
14392 } else if(rnum
> 4) {
14393 self
->xdir
= -self
->modeldata
.speed
;
14397 rnum
= rand32() & 7;
14400 self
->zdir
= -self
->modeldata
.speed
/ 2;
14402 } else if(rnum
> 5) {
14404 self
->zdir
= self
->modeldata
.speed
/ 2;
14409 adjust_walk_animation(target
);
14411 self
->xdir
= self
->zdir
= 0;
14418 //A.I chracter pickup an item
14419 void common_pickupitem(entity
* other
) {
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
;
14434 self
->takeaction
= common_get
;
14435 self
->xdir
= self
->zdir
= 0; //stop moving
14439 else if(self
->weapent
== NULL
&& isSubtypeProjectile(other
) && validanim(self
, ANI_GET
)) {
14441 self
->weapent
= other
;
14442 ent_set_anim(self
, ANI_GET
, 0);
14444 self
->takeaction
= common_get
;
14445 self
->xdir
= self
->zdir
= 0; //stop moving
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);
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
;
14461 //sound_play_sample(SAMPLE_GET, 0, savedata.effectvol,savedata.effectvol, 100);
14463 // else if, TODO: other effects
14465 other
->takeaction
= suicide
;
14466 other
->nextthink
= borTime
+ GAME_SPEED
* 3;
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
;
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
);
14495 self
->direction
= (self
->x
< target
->x
);
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
;
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
) {
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;
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
);
14545 //walk/run/pickup/jump etc
14546 //Used by avoid A.I. pattern
14549 entity
*other
= NULL
; //item
14550 entity
*target
= NULL
; //hostile target
14551 entity
*owner
= NULL
;
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
) {
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
);
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
;
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
);
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;
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
);
14614 //walk/run/pickup/jump etc
14615 //Used by chase A.I. pattern
14618 entity
*other
= NULL
; //item
14619 entity
*target
= NULL
; //hostile target
14620 entity
*owner
= NULL
;
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
) {
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
);
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
;
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
);
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;
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
);
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
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
;
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
) {
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;
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
;
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
);
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
);
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;
14769 // for common arrow types
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)) )
14790 // new subtype chase
14791 if(self
->modeldata
.subtype
== SUBTYPE_CHASE
) {
14792 target
= homing_find_target(self
->modeldata
.hostile
);
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;
14803 self
->xdir
= self
->zdir
= 0;
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
;
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);
14828 ent_set_anim(self
, ANI_IDLE
, 0);
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
;
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);
14863 self
->autokill
= 1;
14864 self
->nextthink
= borTime
+ 1;
14866 self
->nextthink
= borTime
+ THINK_SPEED
/ 2;
14870 // for common bomb types
14872 /*if(!(self->x > level->width - 30 - (PLAYER_MAX_Z-self->z)))
14874 if(self
->direction
== 0)
14875 self
->xdir
= -self
->modeldata
.speed
;
14876 else if(self
->direction
== 1)
14877 self
->xdir
= self
->modeldata
.speed
;
14880 self
->nextthink
= borTime
+ THINK_SPEED
/ 2;
14882 if(inair(self
) && self
->toexplode
== 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
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
;
14909 if(self
->x
< advancex
- 80 || self
->x
> advancex
+ (videomodes
.hRes
+ 80)
14910 || (self
->base
<= 0 && !self
->modeldata
.falldie
)) {
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;
14945 //dispatch move patterns
14946 int common_move() {
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();
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
14981 void common_think() {
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
;
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();
14999 if(self
->link
&& !self
->grabbing
&& !self
->inpain
&& self
->takeaction
!= common_prethrow
&&
15000 borTime
>= self
->stalltime
&& validanim(self
, ANI_SPECIAL
)) {
15005 if(self
->grabbing
&& !self
->attacking
) {
15006 common_grab_check();
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);
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())
15032 //////////////////////////////////////////////////////////////////////////
15035 if(borTime
< self
->stalltime
)
15037 level_completed
|= self
->boss
;
15043 // Re-enter playfield
15044 // Used by player_fall and player_takedamage
15045 void player_die() {
15046 int playerindex
= self
->playerindex
;
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)
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;
15082 spawnplayer(playerindex
);
15083 execute_respawn_script(playerindex
);
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])))) {
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;
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);
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];
15173 self
->health
-= self
->modeldata
.animation
[ANI_SPECIAL
]->energycost
[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() {
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
;
15196 if(check_special()) {
15197 self
->stalltime
= 0;
15198 player
[(int) self
->playerindex
].playkeys
-= thekey
;
15206 void common_land() {
15207 self
->xdir
= self
->zdir
= 0;
15208 if(self
->animating
)
15212 self
->takeaction
= NULL
;
15216 //animal run when you lost it 3 times by tails
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)) {
15226 if(self
->direction
)
15227 self
->x
+= self
->modeldata
.speed
;
15229 self
->x
-= self
->modeldata
.speed
;
15233 void player_blink() {
15235 if(borTime
>= self
->stalltime
)
15240 void common_grabattack() {
15241 if(self
->animating
)
15244 self
->attacking
= 0;
15246 if(!(self
->combostep
[0] || self
->combostep
[1] ||
15247 self
->combostep
[2] || self
->combostep
[3] || self
->combostep
[4])) {
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
;
15260 memset(self
->combostep
, 0, sizeof(int) * 5);
15262 self
->takeaction
= NULL
;
15267 void common_vault() {
15270 self
->takeaction
= NULL
;
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
;
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
;
15294 void common_prejump() {
15295 if(self
->animating
)
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;
15310 self
->takeaction
= common_prejump
;
15312 dojump(jumpv
, jumpx
, jumpz
, jumpid
);
15317 void dojump(float jumpv
, float jumpx
, float jumpz
, int jumpid
) {
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
);
15331 ent_set_anim(self
, jumpid
, 0);
15335 if(self
->direction
== 0)
15336 self
->xdir
= -jumpx
;
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];
15353 self
->health
-= self
->modeldata
.animation
[s
]->energycost
[0];
15356 self
->xdir
= self
->zdir
= 0;
15357 set_attacking(self
);
15359 memset(self
->combostep
, 0, sizeof(int) * 5);
15361 self
->movestep
= 0;
15362 ent_set_anim(self
, s
, 0);
15363 self
->takeaction
= common_attack_proc
;
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
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
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
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
15397 return 0; // Found, but cost more health than the player had
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
15413 self
->modeldata
.special
[i
][MAX_SPECIAL_INPUTS
- (1 + value
)] = 0; // Marks all invalid directional combos with a 0
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
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
15431 return 0; // Found, but cost more health than the player had
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
15446 self
->zdir
= -self
->modeldata
.speed
* 1.75;
15448 self
->zdir
= self
->modeldata
.speed
* 1.75;
15450 } else // Once done animating, returns to thinking
15452 self
->xdir
= self
->zdir
= 0;
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);
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
;
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
;
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
) {
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
) {
15527 self
->weapent
= other
;
15529 sound_play_sample(samples
.get
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
15530 } else if(other
->modeldata
.credit
) {
15534 player
[(int) self
->playerindex
].credits
++;
15536 sound_play_sample(samples
.oneup
, 0, savedata
.effectvol
, savedata
.effectvol
, 100);
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;
15552 void player_fall_check() {
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
))
15566 if(self
->base
!= other
->base
) { // Change this from ->a to ->base
15569 self
->takeaction
= NULL
;
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
;
15577 self
->attacking
= 1;
15578 self
->takeaction
= common_attack_proc
;
15581 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK2
) && check_combo(FLAG_ATTACK2
)) {
15582 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK2
;
15584 self
->attacking
= 1;
15585 self
->takeaction
= common_attack_proc
;
15588 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK3
) && check_combo(FLAG_ATTACK3
)) {
15589 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK3
;
15591 self
->attacking
= 1;
15592 self
->takeaction
= common_attack_proc
;
15595 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK4
) && check_combo(FLAG_ATTACK4
)) {
15596 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK4
;
15598 self
->attacking
= 1;
15599 self
->takeaction
= common_attack_proc
;
15602 if((player
[(int) self
->playerindex
].playkeys
& FLAG_JUMP
) && check_combo(FLAG_JUMP
)) {
15603 player
[(int) self
->playerindex
].playkeys
-= FLAG_JUMP
;
15605 self
->attacking
= 1;
15606 self
->takeaction
= common_attack_proc
;
15609 if((player
[(int) self
->playerindex
].playkeys
& FLAG_SPECIAL
) && check_combo(FLAG_SPECIAL
)) {
15610 player
[(int) self
->playerindex
].playkeys
-= FLAG_SPECIAL
;
15612 self
->attacking
= 1;
15613 self
->takeaction
= common_attack_proc
;
15617 // End cancel checking
15619 if(player_check_special())
15622 if(!nolost
&& self
->modeldata
.weaploss
[0] <= 0)
15626 if(self
->animation
== self
->modeldata
.animation
[ANI_GRABTURN
]) {
15627 // still turning? don't bother with anything else
15628 if(self
->animating
)
15631 // done turning? switch directions and return to grab animation
15633 if(self
->direction
) {
15634 self
->direction
= 0;
15635 other
->direction
= 1;
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);
15663 ent_set_anim(other
, ANI_PAIN
, 0);
15664 other
->xdir
= other
->zdir
= self
->xdir
= self
->zdir
= 0;
15665 other
->x
= self
->x
;
15668 // otherwise, just turn around
15670 if(self
->direction
) {
15671 self
->direction
= 0;
15672 other
->direction
= 1;
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
) {
15687 self
->takeaction
= NULL
;
15691 self
->releasetime
= borTime
+ (GAME_SPEED
/ 2);
15693 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK
) &&
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
))
15700 else if(validanim(self
, ANI_THROW
)) {
15701 if(self
->modeldata
.throwframewait
>= 0)
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
;
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
;
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
;
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
;
15735 else if((player
[(int) self
->playerindex
].playkeys
& FLAG_JUMP
) && validanim(self
, ANI_VAULT
)) {
15736 player
[(int) self
->playerindex
].playkeys
-= FLAG_JUMP
;
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
))
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
);
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
])) {
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;
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;
15773 self
->zdir
= self
->modeldata
.speed
/ 2;
15774 } else if(!(player
[(int) self
->playerindex
].keys
& (FLAG_MOVEUP
| FLAG_MOVEDOWN
)))
15778 if(player
[(int) self
->playerindex
].keys
& FLAG_MOVELEFT
) {
15779 if(self
->modeldata
.grabwalkspeed
)
15780 self
->xdir
= -self
->modeldata
.grabwalkspeed
;
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
;
15789 self
->xdir
= self
->modeldata
.speed
;
15792 ((player
[(int) self
->playerindex
].keys
& FLAG_MOVELEFT
)
15793 || (player
[(int) self
->playerindex
].keys
& FLAG_MOVERIGHT
)))
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);
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);
15821 ent_set_anim(other
, ANI_PAIN
, 0);
15823 ent_set_anim(self
, ANI_GRAB
, 0);
15824 if(validanim(other
, ANI_GRABBED
))
15825 ent_set_anim(other
, ANI_GRABBED
, 0);
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;
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
)) {
15850 self
->mp
-= self
->modeldata
.animation
[ANI_JUMPSPECIAL
]->energycost
[0];
15852 } else if(check_energy(0, ANI_JUMPSPECIAL
)) {
15855 self
->modeldata
.animation
[ANI_JUMPSPECIAL
]->energycost
[0];
15857 } else if(validanim(self
, ANI_JUMPCANT
)) {
15858 player
[(int) self
->playerindex
].playkeys
-= FLAG_SPECIAL
;
15859 ent_set_anim(self
, ANI_JUMPCANT
, 0);
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
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;
15881 self
->takeaction
= common_jump
;
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
;
15954 self
->takeaction
= common_jump
;
15957 if((player
[(int) self
->playerindex
].playkeys
& FLAG_SPECIAL
) && check_combo(FLAG_SPECIAL
)) {
15958 player
[(int) self
->playerindex
].playkeys
-= FLAG_SPECIAL
;
15960 self
->takeaction
= common_jump
;
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);
15967 self
->takeaction
= common_jump
;
15970 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK3
) && check_combo(FLAG_ATTACK3
)) {
15971 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK3
;
15973 self
->takeaction
= common_jump
;
15976 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK4
) && check_combo(FLAG_ATTACK4
)) {
15977 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK4
;
15979 self
->takeaction
= common_jump
;
15983 // End cancel checking.
15988 void player_pain_check() {
15989 if(player_check_special())
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);
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;
16017 self
->takeaction
= NULL
;
16021 void player_preinput() {
16022 float altdiff
; // Used to check that
16023 int notinair
; // entity is not in the air
16025 static const int check_flags
[] = {
16033 static const int max_check_flags
= 6;
16035 static const int check_flags2
[] = {
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
16053 self
->movestep
= 0;
16055 if(self
->direction
)
16056 self
->lastmove
= FLAG_BACKWARD
;
16058 self
->lastmove
= FLAG_FORWARD
;
16061 if(check_combo(check_flags2
[i
]))
16062 ++self
->movestep
; // Check the combo and increase movestep if valid
16064 self
->movestep
= 0;
16066 self
->lastmove
= check_flags2
[i
];
16068 self
->movetime
= borTime
+ (GAME_SPEED
/ 4);
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
;
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
;
16098 if(player
[(int) self
->playerindex
].ent
!= self
|| self
->dead
)
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
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();
16130 if(self
->inpain
|| (self
->link
&& !self
->grabbing
)) {
16131 player_pain_check();
16134 // falling? check for landing
16135 if(self
->projectile
== 2) {
16136 player_fall_check();
16139 // grab section, dont move if still animating
16140 if(self
->grabbing
&& !self
->attacking
&& self
->takeaction
!= common_throw_wait
) {
16141 player_grab_check();
16145 if(self
->jumping
) {
16146 player_jump_check();
16150 if(self
->drop
&& self
->a
== self
->base
&& !self
->tossv
) {
16151 player_lie_check();
16155 // cant do anything if busy
16156 if(!self
->idling
&& !(self
->animation
->idle
&& self
->animation
->idle
[self
->animpos
])) {
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
;
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
;
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
;
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;
16190 self
->zdir
= (float) -0.1;
16191 ent_set_anim(self
, ANI_DODGE
, 0);
16192 self
->takeaction
= common_dodge
;
16194 } else if(check_combo(FLAG_MOVEUP
))
16195 ++self
->movestep
; // Check the combo and increase movestep if valid
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
;
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
;
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;
16216 self
->zdir
= (float) 0.1; //used for checking
16217 ent_set_anim(self
, ANI_DODGE
, 0);
16218 self
->takeaction
= common_dodge
;
16220 } else if(check_combo(FLAG_MOVEDOWN
))
16221 ++self
->movestep
; // Check the combo and increase movestep if valid
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
;
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
16249 self
->movestep
= 0;
16251 if(self
->direction
)
16252 self
->lastmove
= FLAG_BACKWARD
;
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
;
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
16279 self
->movestep
= 0;
16281 if(!self
->direction
)
16282 self
->lastmove
= FLAG_BACKWARD
;
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
;
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
;
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
;
16329 if(check_combo(FLAG_SPECIAL
)) {
16330 player
[(int) self
->playerindex
].playkeys
-= FLAG_SPECIAL
;
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
;
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
+
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);
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
;
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
;
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;
16401 ent_set_anim(self
, ANI_RUNATTACK
, 0);
16402 self
->takeaction
= common_attack_proc
;
16405 // Perform special move, Now checks custom combos
16406 if(check_combo(FLAG_ATTACK
)) {
16407 //player[(int)self->playerindex].playkeys -= FLAG_SPECIAL;
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
;
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;
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.
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
);
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);
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
) {
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;
16478 ent_set_anim(self
, ANI_RUNSLIDE
, 0);
16479 self
->takeaction
= common_attack_proc
;
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
);
16496 if(check_combo(FLAG_JUMP
)) { // Jump can now be used with freespecials
16497 //player[(int)self->playerindex].playkeys -= FLAG_SPECIAL;
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;
16506 ent_set_anim(self
, ANI_SLIDE
, 0);
16507 self
->takeaction
= common_attack_proc
;
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
);
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
);
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);
16542 self
->blocking
= 0;
16545 // check attack2 - attack4 freespecial
16547 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK2
) && check_combo(FLAG_ATTACK2
)) {
16548 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK2
;
16551 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK3
) && check_combo(FLAG_ATTACK3
)) {
16552 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK3
;
16555 if((player
[(int) self
->playerindex
].playkeys
& FLAG_ATTACK4
) && check_combo(FLAG_ATTACK4
)) {
16556 player
[(int) self
->playerindex
].playkeys
-= FLAG_ATTACK4
;
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
) {
16568 self
->zdir
= -self
->modeldata
.speed
/ 2; // Used for up animation
16569 } else if(self
->running
) {
16571 self
->zdir
= -self
->modeldata
.runspeed
/ 2; // Moves up at a faster rate running
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
) {
16582 self
->zdir
= self
->modeldata
.speed
/ 2; // Used for down animation
16583 } else if(self
->running
) {
16585 self
->zdir
= self
->modeldata
.runspeed
/ 2; // Moves down at a faster rate running
16588 self
->zdir
= self
->modeldata
.speed
/ 2;
16590 } else if(!(player
[(int) self
->playerindex
].keys
& (FLAG_MOVEUP
| FLAG_MOVEDOWN
)))
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;
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
)) {
16607 ent_set_anim(self
, ANI_TURN
, 0);
16608 self
->takeaction
= common_turn
;
16611 self
->direction
= 0;
16612 } else if(!self
->modeldata
.turndelay
&& validanim(self
, ANI_TURN
)) {
16614 ent_set_anim(self
, ANI_TURN
, 0);
16615 self
->takeaction
= common_turn
;
16617 } else if(!self
->turntime
)
16618 self
->direction
= 0;
16620 self
->turntime
= 0;
16622 if(self
->running
) {
16624 self
->xdir
= -self
->modeldata
.runspeed
; // If running, player moves at a faster rate
16625 } else if(action
!= 2 && action
!= 3) {
16627 self
->xdir
= -self
->modeldata
.speed
;
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
)) {
16640 ent_set_anim(self
, ANI_TURN
, 0);
16641 self
->takeaction
= common_turn
;
16644 self
->direction
= 1;
16645 } else if(!self
->modeldata
.turndelay
&& validanim(self
, ANI_TURN
)) {
16647 ent_set_anim(self
, ANI_TURN
, 0);
16648 self
->takeaction
= common_turn
;
16650 } else if(!self
->turntime
)
16651 self
->direction
= 1;
16653 self
->turntime
= 0;
16655 if(self
->running
) {
16657 self
->xdir
= self
->modeldata
.runspeed
; // If running, player moves at a faster rate
16658 } else if(action
!= 2 && action
!= 3) {
16660 self
->xdir
= self
->modeldata
.speed
;
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
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
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
)
16688 else if((self
->modeldata
.facing
== 3 || level
->facing
== 3)
16689 && (level
->scrolldir
& SCROLL_RIGHT
) && self
->direction
)
16691 else if(self
->turntime
&& self
->modeldata
.turndelay
)
16694 common_backwalk_anim(self
); //ent_set_anim(self, ANI_BACKWALK, 0);
16696 common_walk_anim(self
); //ent_set_anim(self, ANI_WALK, 0); // If neither up nor down exist, set to walk
16698 common_walk_anim(self
); //ent_set_anim(self, ANI_WALK, 0); // If neither up nor down exist, set to walk
16701 common_up_anim(self
); //ent_set_anim(self, ANI_UP, 0); // Set to up animation if exists
16704 common_down_anim(self
); //ent_set_anim(self, ANI_DOWN, 0); // Set to down animation if exists
16707 ent_set_anim(self
, ANI_RUN
, 0); // Set to run animation if exists
16711 common_idle_anim(self
);
16717 self
->takeaction
= NULL
;
16722 int common_idle_anim(entity
* ent
) {
16725 Damon Vaughn Caskey
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.
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.
16769 int common_walk_anim(entity
* ent
) {
16772 Damon Vaughn Caskey
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.
16803 int common_backwalk_anim(entity
* ent
) {
16805 common_backwalk_anim
16806 Damon Vaughn Caskey
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.
16837 int common_up_anim(entity
* ent
) {
16840 Damon Vaughn Caskey
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.
16871 int common_down_anim(entity
* ent
) {
16874 Damon Vaughn Caskey
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.
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;
16918 void dropweapon(int flag
) {
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
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];
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);
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
;
16952 self
->weapent
->modeldata
.type
= TYPE_NONE
;
16953 self
->weapent
->think
= runanimal
;
16955 self
->weapent
->nextthink
= borTime
+ 1;
16958 self
->weapent
= NULL
;
16961 if(self
->modeldata
.type
== TYPE_PLAYER
) {
16962 if(player
[(int) self
->playerindex
].weapnum
)
16963 set_weapon(self
, player
[(int) self
->playerindex
].weapnum
, 0);
16965 set_weapon(self
, level
->setweap
, 0);
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
) {
16978 //printf("damaged by: '%s' %d\n", other->name, attack->attack_force);
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() {
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
];
17005 ent_list
[i
]->xdir
= (self
->direction
) ? (-1.2) : 1.2;
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;
17019 // Called when boss dies
17020 void kill_all_enemies() {
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;
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
);
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
);
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
);
17066 self
->health
-= attack
->attack_force
;
17067 if(self
->health
<= 0)
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];
17078 self
->health
-= self
->modeldata
.animation
[ANI_SPECIAL
]->energycost
[0];
17085 ////////////////////////////////
17087 void anything_walk() {
17088 if(self
->x
< advancex
- 80 || self
->x
> advancex
+ (videomodes
.hRes
+ 80)) {
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
) {
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
;
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
;
17116 } else if(index
>= 0 || name
) {
17117 dest_index
= index
;
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
);
17127 e
->ptype
= dest_ptype
;
17134 else if(self
->modeldata
.type
== TYPE_PLAYER
)
17135 e
->modeldata
.type
= TYPE_SHOT
;
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
17150 //e->direction = direction;
17151 e
->think
= common_think
;
17152 e
->nextthink
= borTime
+ 1;
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
);
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;
17183 void bomb_explode() {
17184 if(self
->animating
)
17190 entity
*bomb_spawn(char *name
, int index
, float x
, float z
, float a
, int direction
, int map
) {
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
);
17201 e
= spawn(x
, z
, a
, direction
, name
, index
, NULL
);
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;
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;
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;
17248 int star_spawn(float x
, float z
, float a
, int direction
) { // added entity to know which star to load
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
;
17263 starname
= "Star"; // this is default star
17265 for(i
= 0; i
< 3; i
++) {
17266 e
= spawn(x
, z
, a
, direction
, starname
, index
, NULL
);
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
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;
17280 e
->takeaction
= NULL
;
17281 e
->modeldata
.aimove
= AIMOVE1_STAR
;
17282 e
->modeldata
.aiattack
= AIATTACK1_NOATTACK
;
17283 e
->remove_on_attack
= e
->modeldata
.remove
;
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;
17302 void steam_think() {
17303 if(!self
->animating
) {
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
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
) {
17333 e
= spawn(x
, z
, a
, 0, "Steam", -1, NULL
);
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)) {
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
17359 if(!self
->animating
)
17363 ////////////////////////////////
17365 //homing arrow find its target
17366 // type : target type
17367 entity
*homing_find_target(int type
) {
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];
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
]) {
17386 || diff(ent_list
[i
]->x
, self
->x
) + diff(ent_list
[i
]->z
, self
->z
) < diff(ent_list
[index
]->x
,
17388 diff(ent_list
[index
]->z
, self
->z
))
17393 return ent_list
[index
];
17398 void bike_crash() {
17400 if(self
->direction
)
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
;
17418 if(self
->a
< PIT_DEPTH
) {
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
;
17444 self
->direction
= tempself
->direction
;
17445 if(self
->takedamage
)
17446 self
->takedamage(self
, attack
);
17448 self
->health
-= attack
->attack_force
;
17459 void obstacle_fall() {
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)
17476 self
->nextthink
= borTime
+ 2;
17481 int obstacle_takedamage(entity
* other
, s_attack
* attack
) {
17482 if(self
->a
<= PIT_DEPTH
) {
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) {
17500 if(other
->x
< self
->x
)
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
17509 self
->think
= obstacle_fly
;
17510 ent_set_anim(self
, ANI_FALL
, 0);
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
17517 toss(self
, self
->modeldata
.jumpheight
/ 1.333);
17518 ent_set_anim(self
, ANI_FALL
, 0);
17521 if(!self
->modeldata
.nodieblink
)
17526 self
->nextthink
= borTime
+ 1;
17529 static void setDestIfSource_char(char* dest
, char source
) {
17533 static void setDestIfSource_int(int* dest
, int source
) {
17538 entity
*smartspawn(s_spawn_entry
* props
) { // 7-1-2005 Entire section replaced with lord balls code
17543 if(props
== NULL
|| level
== 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
))) {
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
,
17557 e
= spawn(props
->x
+ advancex
, props
->z
, props
->a
, props
->flip
, props
->name
, props
->index
,
17564 //printf("%s, (%f, %f, %f) - (%f, %f, %f)", props->name, props->x, props->z, props->a, e->x, e->z, e->a);
17567 if(props
->alias
[0])
17568 strncpy(e
->name
, props
->alias
, MAX_NAME_LEN
);
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
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
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);
17614 //ent_default_init(e);
17615 execute_onspawn_script(e
);
17616 execute_spawn_script(props
, e
);
17618 } // 7-1-2005 replaced section ends here
17622 void spawnplayer(int index
) {
17624 //s_model * model = NULL;
17626 int xc
, zc
, find
= 0;
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
;
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]);
17644 p
.x
= (float) ((videomodes
.hRes
- 20) - 30 * index
);
17646 if(level
->spawn
[index
][0])
17647 p
.x
= (float) (level
->spawn
[index
][0]);
17649 p
.x
= (float) (20 + 30 * index
);
17652 if(level
->spawn
[index
][1]) {
17653 if(level
->scrolldir
& (SCROLL_INWARD
| SCROLL_OUTWARD
))
17654 p
.z
= (float) (level
->spawn
[index
][1]);
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);
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
;
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
)
17671 if(checkhole(advancex
+ p
.x
, p
.z
) || (wall
>= 0 && level
->walls
[wall
].alt
>= MAX_WALL_HEIGHT
))
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
) {
17685 } else if(wall
>= 0 && level
->walls
[wall
].alt
>= MAX_WALL_HEIGHT
)
17687 if(checkhole(advancex
+ p
.x
, p
.z
))
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
];
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);
17723 set_weapon(player
[index
].ent
, level
->setweap
, 0);
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
) {
17743 for(i
= 0; i
< 4; i
++) {
17744 if(player
[i
].ent
) {
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
17761 // ----------------------- Update functions ------------------------------
17763 void update_scroller() {
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
)
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
;
17799 if(level
->spawnpoints
[current_spawn
].wait
) {
17800 level
->waiting
= 1;
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
;
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)
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)
17838 smartspawn(&level
->spawnpoints
[current_spawn
]);
17843 for(i
= 0; i
< maxplayers
[current_set
]; i
++) {
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
;
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
;
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
;
17896 if(level
->width
- level
->pos
> videomodes
.hRes
)
17897 level
->pos
= (int) advancex
;
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
;
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
;
17931 if(level
->width
- level
->pos
> videomodes
.hRes
)
17932 level
->pos
= (int) ((level
->width
- videomodes
.hRes
) - advancex
);
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
;
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
;
17968 if(panel_height
- level
->pos
> videomodes
.vRes
)
17969 level
->pos
= (int) advancey
;
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
;
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
;
18002 if(panel_height
- level
->pos
> videomodes
.vRes
)
18003 level
->pos
= (int) ((panel_height
- videomodes
.vRes
) - advancey
);
18007 //up down, elevator stage
18008 else if(level
->scrolldir
& (SCROLL_UP
| SCROLL_DOWN
)) {
18010 if(scrolladd
== 1) {
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));
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
);
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
;
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
);
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
;
18102 s_drawmethod screenmethod
;
18105 bgtravelled
+= (((borTime
- traveltime
) * level
->bgspeed
/ 30 * 4) + ((level
->rocking
) ? ((borTime
- traveltime
) / (GAME_SPEED
/ 30)) : 0)); // no like in real life, maybe
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
)
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
);
18135 z
= (int) (4 + bglayer
->zoffset
+ (advancey
- 4) * bglayer
->zratio
- advancey
);
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;
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
);
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
;
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
)
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
);
18206 z
= (int) (4 + fglayer
->zoffset
+ (advancey
- 4) * fglayer
->zratio
- advancey
);
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;
18230 spriteq_add_frame(l
, z
, FRONTPANEL_Z
+ fglayer
->z
, fglayer
->sprite
, &screenmethod
, 0);
18240 void draw_scrolled_bg() {
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
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
;
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
;
18285 bgbuffer_updated
= 0;
18286 //font_printf(2, 100, 1, 0, "%d", bgbuffer_updated);
18289 pbgscreen
= bgbuffer_updated
? vscreen
: bgbuffer
;
18291 pbgscreen
= vscreen
;
18293 if(!bgbuffer_updated
&& level
->numbglayers
> 0)
18294 applybglayers(pbgscreen
);
18295 applyfglayers(pbgscreen
);
18297 // Append bg with 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
);
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;
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;
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
);
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);
18359 if(level
->scrolldir
== SCROLL_UP
|| level
->scrolldir
== SCROLL_DOWN
)
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
,
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
,
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
)
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);
18414 if(level
->quake
!= 0 && borTime
>= level
->quaketime
) {
18416 level
->quaketime
= borTime
+ (GAME_SPEED
/ 25);
18420 void inputrefresh() {
18424 control_update(playercontrolpointers
, maxplayers
[current_set
]);
18427 for(p
= 0; p
< maxplayers
[current_set
]; p
++) {
18428 if(playercontrolpointers
[p
]->newkeyflags
& FLAG_ESC
) {
18434 movie_update(playercontrolpointers
);
18435 font_printf(2, 2, 1, 0, "Playing movie, frames: %d/%d",
18436 movieloglen
+ moviebufptr
- MOVIEBUF_LEN
, movielen
);
18441 control_update(playercontrolpointers
, maxplayers
[current_set
]);
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
);
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
) {
18476 void execute_keyscripts() {
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
)) {
18482 execute_level_key_script(p
);
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() {
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
);
18526 font_printf(textobj
->x
, textobj
->y
, textobj
->font
, textobj
->z
, textobj
->text
);
18531 void update(int ingame
, int usevwait
) {
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
;
18545 newtime
= borTime
+ interval
;
18548 if(slowmotion
[2] == (slowmotion
[1] + 1)) {
18550 if(slowmotion
[0] > 1)
18551 slowmotion
[1] = slowmotion
[0];
18553 if(newtime
> borTime
+ 100)
18554 newtime
= borTime
+ 100;
18556 while(borTime
< newtime
) {
18560 if(level
->settime
> 0
18561 || (!player
[0].ent
&& !player
[1].ent
&& !player
[2].ent
&& !player
[3].ent
)) {
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
)
18578 if(ingame
|| selectScreen
)
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);
18600 draw_scrolled_bg();
18603 execute_updatedscripts();
18606 clearscreen(vscreen
);
18608 putscreen(vscreen
, background
, 0, 0, NULL
);
18610 if(ingame
== 1 || selectScreen
)
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]) {
18623 font_printf(0, 230, 0, 0, debug_msg
);
18624 spriteq_draw(vscreen
, (ingame
== 0));
18629 debug_printf("Position: %i, width: %i, spawn: %i, offsets: %i/%i", level
->pos
, level
->width
,
18630 current_spawn
, level
->quake
, gfx_y_offset
);
18636 video_copy_screen(vscreen
);
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
18653 int set_color_correction(int gm
, int br
) {
18655 vga_set_color_correction(gm
, br
);
18657 } else if(screenformat
== PIXEL_8
) {
18658 palette_set_corrected(pal
, savedata
.gamma
, savedata
.gamma
, savedata
.gamma
, savedata
.brightness
,
18659 savedata
.brightness
, savedata
.brightness
);
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);
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
));
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
) {
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];
18710 for(i
= 0, j
= 0; j
< 64;) {
18712 if(!type
|| type
== 1) {
18713 b
= ((savedata
.brightness
+ 256) * (64 - j
) / 64) - 256;
18714 g
= 256 - ((savedata
.gamma
+ 256) * (64 - j
) / 64);
18716 if(!set_color_correction(g
, b
))
18717 _fade_screen(vscreen
, g
, savedata
.gamma
, savedata
.gamma
, b
, b
, b
);
18720 if(!type
|| type
== 1) {
18721 video_copy_screen(vscreen
);
18724 if(!type
|| type
== 2) {
18725 sound_update_music();
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;
18738 if(!type
|| type
== 2) {
18740 sound_close_music();
18743 if(!type
|| type
== 1) {
18744 clearscreen(vscreen
);
18745 video_copy_screen(vscreen
);
18747 //the black screen, so we return to normal palette
18748 set_color_correction(savedata
.gamma
, savedata
.brightness
);
18754 void apply_controls() {
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
;
18782 if(savedata
.logo
!= 1)
18785 unload_background();
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");
18829 done
|= (borTime
> finishtime
);
18830 done
|= (bothnewkeys
& (FLAG_START
+ FLAG_ESC
));
18837 void borShutdown(const char *caller
, int status
, char *msg
, ...) {
18838 PLOG("shutdown called from %s\n", caller
);
18839 char buf
[1024] = "";
18842 va_start(arglist
, msg
);
18843 vsprintf(buf
, msg
, arglist
);
18848 PLOG("\n************ Shutting Down ************\n\n");
18851 PLOG("\n********** An Error Occurred **********"
18852 "\n* Shutting Down *\n\n");
18860 if(status
!= 2) ; //display_credits();
18864 PLOG("Release level data");
18866 unload_levelorder();
18867 PLOG("...........");
18872 PLOG("Release graphics data");
18875 freescreen(&vscreen
); // allocated by init_videomodes
18878 freescreen(&background
);
18885 unload_all_fonts();
18889 PLOG("Release game data............\n\n");
18899 PLOG("\nRelease game data............\tDone!\n");
18901 PLOG("Release timer................");
18906 PLOG("Release input hardware.......");
18911 PLOG("Release sound system.........");
18916 PLOG("Release FileCaching System...");
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
);
18928 freeCommandList(levelcmdlist
);
18929 if(levelordercmdlist
)
18930 freeCommandList(levelordercmdlist
);
18931 if(scriptConstantsCommandList
)
18932 freeCommandList(scriptConstantsCommandList
);
18936 freefilenamecache();
18938 PLOG("\n**************** Done *****************\n\n");
18941 assert(status
== 0); // this way we can haz backtrace.
18950 printf("FileCaching System Init......\t");
18952 printf("Enabled\n");
18954 printf("Disabled\n");
18956 loadHighScoreFile();
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");
18967 printf("Loading fonts................\t");
18971 printf("Timer init...................\t");
18975 printf("Initialize Sound..............\t");
18976 if(savedata
.usesound
&& sound_init(12)) {
18977 if(load_special_sounds())
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
);
18986 shutdown(1, "Unable to Initialize Sound.\n");
18988 printf("Loading sprites..............\t");
18989 load_special_sprites();
18992 printf("Loading level order..........\t");
18996 printf("Loading script settings......\t");
18997 load_script_setting();
19000 printf("Loading scripts..............\t");
19004 printf("Loading models...............\n\n");
19007 printf("Object engine init...........\t");
19009 shutdown(1, (char*) E_OUT_OF_MEMORY
);
19012 printf("Input init...................\t");
19013 control_init(savedata
.usejoy
);
19019 for(i
= 0; i
< MAX_PAL_SIZE
/ 4; i
++)
19021 if(savedata
.logo
++ > 10)
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 };
19038 u32 temptime
, tempnewtime
; // temporary patch for ingame gif play
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
;
19056 temptime
= borTime
;
19057 tempnewtime
= newtime
;
19063 code
= ANIGIF_DECODE_RETRY
;
19065 synctosound
= (sound_getinterval() != 0xFFFFFFFF);
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;
19078 if(code
== ANIGIF_DECODE_END
)
19083 if(!background
->palette
) {
19084 palette_set_corrected(gifpal
, savedata
.gamma
, savedata
.gamma
, savedata
.gamma
,
19085 savedata
.brightness
, savedata
.brightness
, savedata
.brightness
);
19094 milliseconds
+= sound_getinterval();
19095 if(milliseconds
== 0xFFFFFFFF)
19099 milliseconds
+= (borTime
- lasttime
) * 1000 / GAME_SPEED
;
19100 lasttime
= borTime
;
19102 if(!noskip
&& (bothnewkeys
& (FLAG_ESC
| FLAG_ANYBUTTON
)))
19107 borTime
= temptime
;
19108 newtime
= tempnewtime
;
19110 freescreen(&background
);
19111 background
= tempbg
;
19112 if(bothnewkeys
& (FLAG_ESC
| FLAG_ANYBUTTON
))
19118 void playscene(char *filename
) {
19122 char *command
= NULL
;
19124 int x
= 0, y
= 0, skipone
= 0, noskip
= 0;
19128 char argbuf
[MAX_ARG_LEN
+ 1] = "";
19131 if(buffer_pakfile(filename
, &buf
, &size
) != 1)
19134 // Now interpret the contents of buf line by line
19137 ParseArgs(&arglist
, buf
+ pos
, argbuf
);
19138 command
= GET_ARG(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
)
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) {
19164 int playback_started
= 0;
19166 music("data/music/gameover", 0, 0);
19170 if(!playback_started
&& testpackfile("data/scenes/gameover.txt", packfile
) >= 0)
19171 playscene("data/scenes/gameover.txt");
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
));
19182 void hallfame(int addtoscore
) {
19184 int topten
[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
19186 char name
[MAX_NAME_LEN
+ 1];
19188 char tmpBuff
[128] = { "" };
19189 s_model
*model
= NULL
;
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);
19200 load_cached_background("data/bgs/hiscore", 0);
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
);
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
);
19217 savescore
.highsc
[i
+ 1] = score
;
19218 strcpy(savescore
.hscoren
[i
+ 1], name
);
19230 font_printf(_strmidx(3, "Hall Of Fame"), y
- font_heights
[3] - 10 + videomodes
.vShift
, 3, 0,
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;
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
) {
19252 u32 clearbonus
[4] = { 10000, 10000, 10000, 10000 };
19253 u32 lifebonus
[4] = { 10000, 10000, 10000, 10000 };
19254 u32 rushbonus
[4] = { 10000, 10000, 10000, 10000 };
19256 u32 finishtime
= 0;
19258 char tmpBuff
[128] = { "" };
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);
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];
19279 clearbonus
[i
] = scbonuses
[0];
19280 lifebonus
[i
] = player
[i
].lives
* scbonuses
[1];
19288 font_printf(videomodes
.hShift
+ scomplete
[0], videomodes
.vShift
+ scomplete
[1], 3, 0,
19289 "Stage %i Complete!", num
);
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",
19294 font_printf(videomodes
.hShift
+ scomplete
[4], videomodes
.vShift
+ scomplete
[5], 3, 0,
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
) {
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) {
19329 clearbonus
[i
] -= 10;
19331 } else if(lifebonus
[i
] > 0) {
19333 lifebonus
[i
] -= 10;
19335 } else if(rush
[0] >= 1 && showrushbonus
== 1 && (rushbonus
[i
] > 0)) {
19337 rushbonus
[i
] -= 10;
19343 if(!finishtime
&& !(nexttime
& 15)) {
19344 sound_stop_sample(chan
);
19346 sound_play_sample(samples
.beep
, 0, savedata
.effectvol
/ 2, savedata
.effectvol
/ 2,
19352 if(bothnewkeys
& (FLAG_ANYBUTTON
| FLAG_ESC
))
19354 if(finishtime
&& borTime
> finishtime
)
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() {
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
) {
19398 Script
*ptempscript
= pcurrentscript
;
19404 saveHighScoreFile();
19407 load_level(filename
);
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
;
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
));
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
));
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
;
19447 sound_close_music();
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
};
19462 int cmap
[MAX_PLAYERS
] = { 0, 1, 2, 3 };
19465 int ready
[MAX_PLAYERS
] = { 0, 0, 0, 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
;
19476 char argbuf
[MAX_ARG_LEN
+ 1] = "";
19480 reset_playable_list(1);
19482 if(loadGameFile()) {
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));
19505 load_cached_model(GET_ARG(1), filename
, GET_INT_ARG(2));
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
);
19515 freeAndNull((void**) &buf
);
19516 for(i
= 0; i
< maxplayers
[current_set
]; i
++) {
19518 if(!psmenu
[i
][0] && !psmenu
[i
][1]) {
19519 if(maxplayers
[current_set
] > 2)
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
));
19530 (83 + (videomodes
.hShift
/ 2) +
19531 (i
* (155 + videomodes
.hShift
))),
19532 (float) (230 + videomodes
.vShift
), 0, spdirection
[i
], NULL
,
19533 -1, nextplayermodel(NULL
));
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
));
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
);
19552 credits
= CONTINUES
;
19554 player
[i
].credits
= CONTINUES
;
19555 player
[i
].hasplayed
= 1;
19557 if(!creditscheat
) {
19559 --player
[i
].credits
;
19563 player
[i
].lives
= PLAYER_LIVES
;
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);
19576 load_cached_background("data/bgs/unlockbg", 1);
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);
19584 load_cached_background("data/bgs/select", 1);
19586 if(!music("data/music/menu", 1, 0))
19587 music("data/music/remix", 1, 0);
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
)) {
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);
19606 if(player
[i
].lives
<= 0 && (noshare
|| credits
> 0)
19607 && ((player
[i
].newkeys
& FLAG_ANYBUTTON
) || immediate
[i
])) {
19609 player
[i
].credits
= CONTINUES
;
19610 player
[i
].hasplayed
= 1;
19613 if(!creditscheat
) {
19615 --player
[i
].credits
;
19620 player
[i
].lives
= PLAYER_LIVES
;
19622 if(!psmenu
[i
][0] && !psmenu
[i
][1]) {
19623 if(maxplayers
[current_set
] > 2)
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
));
19634 (83 + (videomodes
.hShift
/ 2) +
19635 (i
* (155 + videomodes
.hShift
))),
19636 (float) (230 + videomodes
.vShift
), 0, spdirection
[i
],
19637 NULL
, -1, nextplayermodel(NULL
));
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
) {
19656 if(cmap
[i
] > example
[i
]->modeldata
.maps_loaded
)
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
);
19671 while((example
[i
]->modeldata
.hmap1
) && (example
[i
]->modeldata
.hmap2
) &&
19672 cmap
[i
] >= example
[i
]->modeldata
.hmap1
&&
19673 cmap
[i
] <= example
[i
]->modeldata
.hmap2
) {
19675 if(cmap
[i
] > example
[i
]->modeldata
.maps_loaded
)
19679 ent_set_colourmap(example
[i
], cmap
[i
]);
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
);
19687 while((example
[i
]->modeldata
.hmap1
) && (example
[i
]->modeldata
.hmap2
) &&
19688 cmap
[i
] >= example
[i
]->modeldata
.hmap1
19689 && cmap
[i
] <= example
[i
]->modeldata
.hmap2
) {
19691 if(cmap
[i
] > example
[i
]->modeldata
.maps_loaded
)
19695 ent_set_colourmap(example
[i
], cmap
[i
]);
19698 // oooh pretty colors! - selectable color scheme for player characters
19699 else if(player
[i
].newkeys
& FLAG_MOVEUP
&& colourselect
&& example
[i
]) {
19702 if(cmap
[i
] > example
[i
]->modeldata
.maps_loaded
)
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
]) {
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
19735 for(x
= 0; x
< maxplayers
[current_set
]; x
++) {
19736 if((i
!= x
) && (!strcmp(player
[i
].name
, player
[x
].name
))) {
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
) {
19750 if((!validanim(example
[i
], ANI_PICK
)
19751 || example
[i
]->modeldata
.animation
[ANI_PICK
]->loop
[0])
19752 && borTime
> GAME_SPEED
* 2)
19754 else if(!example
[i
]->animating
)
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!");
19769 font_printf(67 + (videomodes
.hShift
/ 2) +
19770 (i
* (155 + videomodes
.hShift
)), 225 + videomodes
.vShift
, 0,
19773 font_printf(psmenu
[i
][2], psmenu
[i
][3], 0, 0, "Ready!");
19776 if(example
[i
] != NULL
)
19782 if(players_busy
&& players_busy
== players_ready
&& borTime
> GAME_SPEED
)
19786 if(bothnewkeys
& FLAG_ESC
)
19790 // No longer at the select screen
19793 sound_close_music();
19798 void playgame(int *players
, unsigned which_set
, int useSavedGame
) {
19802 current_set
= which_set
;
19804 if(which_set
>= num_difficulties
)
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)
19820 sameplayer
= same
[which_set
];
19822 memset(player
, 0, sizeof(s_player
) * 4);
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
;
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) {
19854 branch_name
[0] = 0; // clear up so we won't stuck here
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) {
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) {
19876 if(!noshowhof
[which_set
])
19878 for(i
= 0; i
< maxplayers
[current_set
]; i
++) {
19879 player
[i
].hasplayed
= 0;
19880 player
[i
].weapnum
= 0;
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;
19892 savelevel
[current_set
].stage
= current_stage
;
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
++;
19905 saveHighScoreFile();
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) {
19929 char *filename
= "data/video.txt";
19934 char *command
= NULL
;
19935 char *value
= NULL
;
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");
19950 if(buffer_pakfile(filename
, &buf
, &size
) != 1) {
19951 videoMode
= VTM_320_240
;
19952 printf("'%s' not found.\n", filename
);
19956 printf("Reading video settings from '%s'.\n", filename
);
19958 // Now interpret the contents of buf line by line
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
;
19981 } else if(stricmp(value
, "32bit") == 0) {
19982 screenformat
= PIXEL_32
;
19984 } else if(value
[0] == 0)
19985 screenformat
= PIXEL_32
;
19987 shutdown(1, "Screen colour depth can only be either 8bit, 16bit or 32bit.");
19993 printf("%s(): Command '%s' is not understood in file '%s', line %u!\n", __FUNCTION__
, command
, filename
, line
);
19996 pos
+= getNewLineStart(buf
+ pos
);
19999 freeAndNull((void**) &buf
);
20002 if(videoMode
>= VTM_MAX
)
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
);
20021 clearscreen(vscreen
);
20022 printf("Initialized video.............\t%dx%d (Mode: %d, Depth: %d Bit)\n\n", videomodes
.hRes
,
20023 videomodes
.vRes
, videoMode
, bits
);
20028 // ----------------------------------------------------------------------------
20030 // Set key or button safely (with switching)
20031 void safe_set(int *arr
, int index
, int newkey
, int oldkey
) {
20033 for(i
= 0; i
< 12; i
++) {
20034 if(arr
[i
] == newkey
)
20037 arr
[index
] = newkey
;
20040 void keyboard_setup(int player_nr
) {
20042 printf("Loading control settings.......\t");
20047 controller_options(player_nr
, &quit
, (char**) &buttonnames
, disabledkey
);
20061 // ----------------------------------------------------------------------------
20063 void openborMain(int argc
, char **argv
) {
20069 char tmpBuff
[128] = { "" };
20070 int players
[MAX_PLAYERS
];
20073 printf("OpenBoR %s, Compile Date: " __DATE__
"\n\n", VERSION
);
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();
20091 // Load necessary components.
20092 printf("Game Selected: %s\n\n", packfile
);
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);
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
)))
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");
20119 introtime
= borTime
+ GAME_SPEED
* 20;
20124 if(bothnewkeys
& FLAG_ESC
)
20128 if((borTime
% GAME_SPEED
) < (GAME_SPEED
/ 2))
20129 _menutextm(0, 0, 0, "PRESS START");
20130 if(bothnewkeys
& (FLAG_ANYBUTTON
)) {
20136 relback
= main_menu(&started
, &introtime
, players
);
20140 if(custBkgrds
!= NULL
) {
20141 strncpy(tmpBuff
, custBkgrds
, 128);
20142 strncat(tmpBuff
, "titleb", 6);
20143 load_background(tmpBuff
, 0);
20145 load_cached_background("data/bgs/titleb", 0);
20147 if(custBkgrds
!= NULL
) {
20148 strncpy(tmpBuff
, custBkgrds
, 128);
20149 strncat(tmpBuff
, "title", 5);
20150 load_background(tmpBuff
, 0);
20152 load_cached_background("data/bgs/title", 0);
20155 if(!sound_query_music(NULL
, NULL
))
20156 music("data/music/remix", 1, 0);
20166 #undef GET_ARGP_LEN
20168 #undef GET_FLOAT_ARG
20169 #undef GET_INT_ARGP
20170 #undef GET_FLOAT_ARGP