/* ----------------------------------------------------------------------------- Project: AWOL - enemy_arena.con Authors: Michelle Sleeper, Riccardo Capogna, Jonathan Strander, Dave Tietz ----------------------------------------------------------------------------- Summary : Combat arena management and spawning. Arena Managers act like Touchplates, activating when a player enters their sector. Arena Spawn Points act like Respawns, except they can spawn multiple times. Once activated, a Manager will attempt to trigger their paired Spawn Points in waves on a repeating cycle. Each wave, a Spawn Point will only trigger if its spawned child is dead, otherwise it will be skipped. Spawn Points will continue spawning until their "tickets" run out. For example, if an arena has 5 Spawn Points but 2 enemies are left alive, the next wave will only spawn 3 enemies, but only if each of those 3 Spawn Points have remaining tickets. For Arena Managers: * Set the sprite's HITAG to the spawn wave frequency, in game tics (min 90) For Arena Spawn Points: * Set the sprite's HITAG to the tile ID to spawn * Set the sprite's LOTAG to the number of spawn "tickets" (min 1) Use the palette to pair a Manager with Spawn Points to create a complete system. A Manager can have any number of associated Spawn Points, and there can be as many Manager systems as there are palette numbers. Note: Spawn Points will not spawn if a player has line-of-sight with them, so place them in non-visible locations, and try not to place other Spawn Points within line-of-sight of each other to prevent the system from dead locking! ----------------------------------------------------------------------------- */ // Init manager and spawn points appendevent EVENT_ENTERLEVEL { for ACTORI allsprites { ife sprite[ACTORI].picnum CONTROL_ARENA { seta[ACTORI].statnum STAT_ARENAMANAGER ifn AWOL_DEBUG TRUE seta[ACTORI].cstat 32768 // Spawn wave frequency, min 90 tics geta[ACTORI].hitag ACTORTEMP clamp ACTORTEMP 90 MAXINT setav[ACTORI].ACTORCOUNTDOWN ACTORTEMP seta[ACTORI].hitag 0 } else ife sprite[ACTORI].picnum CONTROL_ARENASPAWN { seta[ACTORI].statnum STAT_ARENASPAWN ifn AWOL_DEBUG TRUE seta[ACTORI].cstat 32768 // Spawn actor setav[ACTORI].ACTORSPAWN sprite[ACTORI].hitag seta[ACTORI].hitag 0 // Spawn amount geta[ACTORI].lotag ACTORTEMP clamp ACTORTEMP 1 MAXINT setav[ACTORI].ACTORCOUNT ACTORTEMP seta[ACTORI].lotag 0 } } ife AWOL_DEBUG TRUE { set ACTORTEMP 0 for ACTORI sprofstat STAT_ARENAMANAGER add ACTORTEMP 1 set ACTORTEMP2 0 for ACTORI sprofstat STAT_ARENASPAWN add ACTORTEMP2 1 redefinequote 254 Found %d arenas and %d spawn points qsprintf 254 254 ACTORTEMP ACTORTEMP2 echo 254 } } endevent defstate arena_manager { ife ACTORSTATUS TRUE { add ACTORCOUNT 1 ifg ACTORCOUNT ACTORCOUNTDOWN { // Used to check if all of our spawn points are out of tickets, to kill the system set ACTORBURST 0 // Store our position for later geta .x MX geta .y MY geta .z MZ geta .sectnum MSECT // Spawn arena actors if enemies are dead for ACTORI sprofstat STAT_ARENASPAWN { // Move to spawn point to perform line-of-sight checks and hide spawning seta .x sprite[ACTORI].x seta .y sprite[ACTORI].y seta .z sprite[ACTORI].z seta .sectnum sprite[ACTORI].sectnum // Skip interpolation seta .htbposx sprite.x seta .htbposy sprite.y seta .htbposz sprite.z // Only spawn for the same arena group ife sprite[].pal sprite[ACTORI].pal set ACTORCHECK TRUE else set ACTORCHECK FALSE // Only spawn if this spawn point has any spawn amounts ife ACTORCHECK TRUE { add ACTORBURST actorvar[ACTORI].ACTORCOUNT ifle actorvar[ACTORI].ACTORCOUNT 0 set ACTORCHECK FALSE } // Spawn if we have no enemy, or our enemy is dead ife ACTORCHECK TRUE { ifn actorvar[ACTORI].ACTORTARGET -1 ifg sprite[actorvar[ACTORI].ACTORTARGET].extra 0 set ACTORCHECK FALSE } // Do not spawn if the player can see the spawn point // DEBUG: Removed per request by Mikko, this seems to be too aggressive. Need to refactor line-of-sight method /* ife ACTORCHECK TRUE { geta .z ACTORTEMP sub ACTORTEMP ACTOREYEHEIGHT seta .z ACTORTEMP ifcansee set ACTORCHECK FALSE seta .z sprite[ACTORI].z } */ // Perform spawn if all checks pass ife ACTORCHECK TRUE { /* // Mark old enemy corpse for deletion // TODO: enable if we add in a "Performance" option ifn actorvar[ACTORI].ACTORTARGET -1 ife PERFORMANCE TRUE { seta[actorvar[ACTORI].ACTORTARGET].xrepeat 0 seta[actorvar[ACTORI].ACTORTARGET].statnum 1 } */ // Spawn replacement enemy espawnvar actorvar[ACTORI].ACTORSPAWN // Update array with new enemy setav[ACTORI].ACTORTARGET RETURN // Make enemy ignore the kill count, since they may spawn indefinitely setav[RETURN].ACTORNOKILL TRUE getp .max_actors_killed ACTORTEMP sub ACTORTEMP 1 setp .max_actors_killed ACTORTEMP // Randomly upgrade enemies ifrnd 84 { ifrnd 84 seta[RETURN].pal 2 else seta[RETURN].pal 21 } // Wake up actor as soon as they are spawned setav[RETURN].ACTORSTATUS STATUS_ALERT setav[RETURN].ACTORCOUNTDOWN MININT // Decrease spawn amount getav[ACTORI].ACTORCOUNT ACTORTEMP sub ACTORTEMP 1 setav[ACTORI].ACTORCOUNT ACTORTEMP } } // Restore our position seta .x MX seta .y MY seta .z MZ seta .z MSECT // Skip interpolation seta .htbposx sprite.x seta .htbposy sprite.y seta .htbposz sprite.z set ACTORCOUNT 0 // No remaining tickets in any spawn points, so die off ife ACTORBURST 0 { seta .xrepeat 0 // invalid size + seta .statnum STAT_ACTOR // actor = delete } } } else ife sprite[].sectnum player[].cursectnum { set ACTORSTATUS TRUE set ACTORCOUNT 0 } } ends appendevent EVENT_WORLD { for NODEI sprofstat STAT_ARENAMANAGER { setu .vm_sprite NODEI setu .vm_player myconnectindex state arena_manager } } endevent