home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Deathday Collection
/
dday.bin
/
menus
/
dmijum
/
jumble.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-04-03
|
26KB
|
675 lines
/*
* JUMBLE.C
*
* Version 1.0.0
*
* Abstract:
* This program creates a file, JUMBLE.WAD, that contains the 27 maps
* from DOOM in a random order. The things (characters, items, etc.) in each
* map are shuffled and start out facing in a random direction. Each map
* has a song randomly assigned to it. The Boss levels are special
* exceptions: they remain in the same place (map 8), but they get new,
* wacky music never before heard in a map. Take note: JUMBLE.WAD is over
* 3 megs in size. E3M8 is handled specially, and it is twisted!
*
* After creating JUMBLE.WAD, run DOOM with this form:
* DOOM -file JUMBLE.WAD { other parameters }
*
* History:
* 1.0.0 (April 3, 1994)
*
* Author:
* Michael McMahon
*
****************************************************************************
* Jumble Function Directory
*---------------------------------------------------------------------------
*Index: Name : Description
*---------------------------------------------------------------------------
* #1 : WadfileCopyAndJumbleMap : Can be used instead of 'WadfileCopyMap'.
****************************************************************************
*
* WADLIB SOFTWARE LICENSE AGREEMENT
*
* 1. GRANT OF LICENSE. Michael McMahon and his affiliations (collectively
* the "AUTHOR") grant you (either an individual or an entity) the
* non-exclusive, royalty-free right to use this library source code,
* documentation, and sample code (collectively, the "SOFTWARE") for
* any lawful purpose subject to the terms of this license. By using the
* SOFTWARE you are agreeing to be bound to all the terms of this license.
*
* 2. COPYRIGHT. The SOFTWARE is Copyright (c) 1994, Michael McMahon,
* PO Box 14807, San Luis Nabisco, CA 93406-4807 USA. All Rights Reserved
* Worldwide. You may not use, modify, or distribute the SOFTWARE except
* as otherwise provided herein.
*
* 3. DECLARATION OF PUBLIC DOMAIN DISTRIBUTION AND USE. The distribution
* and use of the SOFTWARE is hereby designated PUBLIC DOMAIN by the
* the AUTHOR. You may not sell, rent, or lease this SOFTWARE. The
* SOFTWARE may be reproduced verbatim in part or in full by any
* reproduction means for any lawful purpose, and may also be subject to
* the following agreement.
*
* 4. AGREEMENT FOR USE OF SOFTWARE. The AUTHOR grants you a non-exclusive,
* royalty-free right to incorporate the SOFTWARE into any production for
* any legal purpose as long as you agree
* (a) to indemnify, hold harmless, and defend the AUTHOR from and against
* any claims or lawsuits, including attorneys' fees, that arise or
* result from the use or distribution of your software production; and
* (b) no matter how much the SOFTWARE is modified, the AUTHOR owns the
* copyright and this original, unmodified copyright notice remains
* intact in the source code; and,
* (c) the AUTHOR is not held responsible for fixing bugs or making
* enhancements or changes to the SOFTWARE for any reason; and,
* (d) the SOFTWARE is not redistributed if it is modified in any way; and,
* (e) otherwise comply with the terms of this agreement; and,
* (f) the AUTHOR is forgiven for making so many demands.
*
* THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE
* AUTHOR FURTHER DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT
* LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR OF FITNESS
* FOR A PARTICULAR PURPOSE. THE ENTIRE RISK ARISING OUT OF THE USE
* OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU.
*
* The author can be reached at:
* Michael McMahon
* P.O. Box 14807
* San Luis Nabisco, CA 93406-4807 USA
* Internet: mmcmahon@oboe.calpoly.edu
* [Bug reports, suggestions, success stories, etc. are welcome; tech
* support, and other unnecessary two-way mail, is not]
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <mem.h>
#include <time.h>
#include "general.h"
#include "wadfile.h"
#include "dm_mapdf.h"
/* Program-specific definitions */
#define PROG_FILENAME "JUMBLE"
#define PROG_AUTHOR "Michael McMahon"
/* Values to be returned to the operating system */
#define EXIT_ERROR -1
#define EXIT_OK 0
/* System bounds */
#define MAX_ENTRIES 500
#define FILE_NAME_SIZE 200
/* Target filename */
#define JUMBLE_NAME "JUMBLE.WAD"
/* Jumble structure */
typedef struct {
int episode;
int map;
int touched;
char musicName[SIZEOF_WAD_DIR_ENTRY];
} JumbledMapRec;
/* Funny BOSS level music */
char * bossEpisodeMusic[DOOM_LASTEPISODE]={"D_VICTOR","D_INTRO\0","D_BUNNY\0"};
/* Maximum allowable THINGS per map */
#define MAX_THINGS 1000
/* Record used to shuffle THINGS */
struct {
int newLocation;
int touched;
} jumbledThings[MAX_THINGS];
/* Symbol used to determine what maps still need to be defined */
#define JUMBLEREC_UNTOUCHED 0
#define JUMBLEREC_TOUCHED 1
/* Thing types */
#define TYPE_PLAYER1START 1
#define TYPE_PLAYER2START 2
#define TYPE_PLAYER3START 3
#define TYPE_PLAYER4START 4
#define TYPE_BLUEKEY 5
#define TYPE_YELLOWKEY 6
#define TYPE_SPIDERDEMON 7
#define TYPE_DEATHMATCHPOS 11
#define TYPE_REDKEY 13
#define TYPE_TELEPORTER 14
#define TYPE_CYBERLORD 16
#define TYPE_CELL_CASE 17
#define TYPE_REDSKULL 38
#define TYPE_YELLOWSKULL 39
#define TYPE_BLUESKULL 40
#define TYPE_INVULNERABILITY 2022
#define TYPE_CELL_CHARGE 2047
#define TYPE_BARON 3003
#define TYPE_HUMAN 3004
#define TYPE_CACODEMON 3005
/* Constants used in randomizing angle THINGS initially face */
#define NUM_FACES 8
#define TOTAL_DEGREES 360
/*------------------------- Internal routines ---------------------------*/
/**[ Internal 1 of 2 ]****************************************************
* *
* wadMalloc *
* *
* Desc: *
* This is the memory allocation routine for this example program. *
* The memory management routines are stored outside WADFILE.C so *
* it would be compatible with any memory manager. This example *
* uses ANSI C's malloc. *
* *
* Def: *
* static void * wadMalloc(unsigned long size); *
* *
* Parm: *
* size Number of bytes to allocate. *
* *
* Retn: *
* Pointer to allocated memory, or NULL if not enough memory. *
* *
* Notes: *
* Use a pointer to this routine as the first parameter in the *
* 'WadfileInitialize' call. *
* *
*************************************************************************/
static void * wadMalloc (unsigned long size)
{
/* Note: If size is > 64k, some implementations of malloc might not work */
return malloc(size);
}
/**[ Internal 2 of 2 ]****************************************************
* *
* wadFree *
* *
* Desc: *
* This is the memory deallocation routine for this example program. *
* The memory management routines are stored outside WADFILE.C so *
* it would be compatible with any memory manager. This example *
* uses ANSI C's free. *
* *
* Def: *
* static void wadFree(void * ptr); *
* *
* Parm: *
* ptr Valid memory pointer that was allocated with wadMalloc. *
* *
* Retn: *
* None *
* *
* Notes: *
* Use a pointer to this routine as the second parameter in the *
* 'WadfileInitialize' call. *
* *
*************************************************************************/
static void wadFree(void * ptr)
{
/* Release memory */
free(ptr);
}
/*------------------------- Utility routines ------------------------------*/
/**[ #1 ]*****************************************************************
* *
* WadfileCopyAndJumbleMap *
* *
* Desc: *
* This copies an arbitrary map from an open Wadfile to a Wadfile *
* that is being created, jumbling the THINGS section. Do not use *
* 'WadfileLumpOpen' or 'WadfileLumpClose' to initialize and release *
* the lump data; this routine is self-contained and opens, copies, *
* and closes the lump data in one fell swoop. *
* *
* Def: *
* int WadfileCopyAndJumbleMap(Wadfile * dest, int destEpisode, *
* int destMap, Wadfile * src, int srcEpisode, int srcMap) *
* *
* Parm: *
* dest A Wadfile that is being created. *
* destEpisode The new episode number for the map data. *
* destMap The new map number for the map data. *
* src An existing Wadfile. *
* srcEpisode The old episode number for the map data. *
* srcMap The old map number for the map data. *
* *
* Retn: *
* TRUE if map is copied succesfully; FALSE otherwise. *
* *
* Notes: *
* On a 16-bit platform, this successfully copies lumps larger *
* than 64k. *
* *
* E3M8 (Spiderdemon) is handled specially. The values are always *
* the same. *
* *
*************************************************************************/
int WadfileCopyAndJumbleMap(Wadfile * dest, int destEpisode, int destMap,
Wadfile * src, int srcEpisode, int srcMap)
{
int i,j,k; /* Loop control and scratchpad variables */
unsigned long location; /* Starting location of lump */
int numItems; /* The count of the items in a lump */
char * ptr; /* Used to access in-memory 'dirCache' */
char targetName[SIZEOF_WAD_DIR_NAME];
ThingRec * allThings; /* THINGS array used in jumbling */
ThingRec thing;
unsigned long numThingsAdded;
numThingsAdded = 0;
/* Seek to map location, if possible */
if (!WadfileSeekMap(src, srcEpisode, srcMap)) return FALSE;
/* Copy the source map to destination map */
if (!WadfileLumpOpen(src)) return FALSE;
sprintf(targetName, "E%dM%d\0\0\0\0", destEpisode, destMap);
if (!WadfileAddLump(dest, src->lumpSize, targetName, src->lumpData))
return FALSE;
if (!WadfileLumpClose(src)) return FALSE;
/* Now that this is copied, go to next entry */
WadfileGetNextDirInfo(src);
/* Copy and jumble the THINGS section */
location = ftell(dest->wf);
/* Position the file to the beginning of the THINGS section */
if (!WadfileLumpOpen(src)) return FALSE;
allThings = (ThingRec *) src->lumpData;
/* How many THINGS? */
numItems = src->entrySize/sizeof(ThingRec);
/* E3M8 is handled elsewhere */
if (!((destEpisode==3)&&(destMap==8)))
{
/* Clear jumbled THINGS record */
for (i=0; i<numItems; i++)
{
/* Exclude certain types from jumbling */
if ((allThings[i].type == TYPE_PLAYER1START) ||
(allThings[i].type == TYPE_PLAYER2START) ||
(allThings[i].type == TYPE_PLAYER3START) ||
(allThings[i].type == TYPE_PLAYER4START) ||
(allThings[i].type == TYPE_DEATHMATCHPOS) ||
(allThings[i].type == TYPE_BLUEKEY) ||
(allThings[i].type == TYPE_YELLOWKEY) ||
(allThings[i].type == TYPE_REDKEY) ||
(allThings[i].type == TYPE_BLUESKULL) ||
(allThings[i].type == TYPE_YELLOWSKULL) ||
(allThings[i].type == TYPE_REDSKULL) ||
(allThings[i].type == TYPE_TELEPORTER))
jumbledThings[i].touched = JUMBLEREC_TOUCHED;
else
jumbledThings[i].touched = JUMBLEREC_UNTOUCHED;
}
/* Jumble the THINGS */
for (i=0; i<numItems; i++)
{
if (!((allThings[i].type == TYPE_PLAYER1START) ||
(allThings[i].type == TYPE_PLAYER2START) ||
(allThings[i].type == TYPE_PLAYER3START) ||
(allThings[i].type == TYPE_PLAYER4START) ||
(allThings[i].type == TYPE_DEATHMATCHPOS) ||
(allThings[i].type == TYPE_BLUEKEY) ||
(allThings[i].type == TYPE_YELLOWKEY) ||
(allThings[i].type == TYPE_REDKEY) ||
(allThings[i].type == TYPE_BLUESKULL) ||
(allThings[i].type == TYPE_YELLOWSKULL) ||
(allThings[i].type == TYPE_REDSKULL) ||
(allThings[i].type == TYPE_TELEPORTER)))
{
/* Find an open spot randomly */
do{
j = rand()%numItems;
} while (jumbledThings[j].touched == JUMBLEREC_TOUCHED);
/* Mark as touched, and store new location */
jumbledThings[j].touched = JUMBLEREC_TOUCHED;
jumbledThings[j].newLocation = i;
}
else
/* Don't move it */
jumbledThings[i].newLocation = i;
}
/* Write the THINGS, one by one, to disk */
for (i=0; i<numItems; i++)
{
/* Fill the 'thing' structure */
thing.x = allThings[jumbledThings[i].newLocation].x;
thing.y = allThings[jumbledThings[i].newLocation].y;
thing.angle = (rand()%NUM_FACES)*(TOTAL_DEGREES/NUM_FACES);
thing.type = allThings[i].type;
thing.flags = allThings[i].flags;
/* Write it to disk */
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
}
}
else
{
/* E3M8 handler */
/* 'j' counts the number of cacodemons processed */
j=0;
/* Write the THINGS, one by one, to disk */
for (i=0; i<numItems; i++)
{
/* Fill the 'thing' structure */
thing.x = allThings[i].x;
thing.y = allThings[i].y;
thing.angle = allThings[i].angle;
switch (allThings[i].type)
{
case TYPE_CACODEMON: j++;
if (j==1)
thing.type = TYPE_CYBERLORD;
else
thing.type = TYPE_SPIDERDEMON;
break;
case TYPE_BARON:
thing.type = TYPE_INVULNERABILITY;
break;
case TYPE_CELL_CHARGE:
thing.type = TYPE_CELL_CASE;
break;
case TYPE_PLAYER2START:
thing.type = TYPE_PLAYER1START;
break;
case TYPE_PLAYER1START:
case TYPE_PLAYER3START:
thing.type = TYPE_BARON;
break;
case TYPE_PLAYER4START:
thing.type = TYPE_HUMAN;
break;
default: thing.type = allThings[i].type;
break;
}
thing.flags = THING_LEVEL12 | THING_LEVEL3 | THING_LEVEL4;
/* Write it to disk */
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
}
/* Add the new player starting locations */
thing.x = -1536;
thing.y = -943;
thing.angle = (rand()%NUM_FACES)*(TOTAL_DEGREES/NUM_FACES);
thing.type = TYPE_PLAYER2START;
thing.flags = THING_LEVEL12 | THING_LEVEL3 | THING_LEVEL4;
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
thing.x = -1651;
thing.y = -968;
thing.angle = (rand()%NUM_FACES)*(TOTAL_DEGREES/NUM_FACES);
thing.type = TYPE_PLAYER3START;
thing.flags = THING_LEVEL12 | THING_LEVEL3 | THING_LEVEL4;
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
thing.x = -1481;
thing.y = -1068;
thing.angle = (rand()%NUM_FACES)*(TOTAL_DEGREES/NUM_FACES);
thing.type = TYPE_PLAYER4START;
thing.flags = THING_LEVEL12 | THING_LEVEL3 | THING_LEVEL4;
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
/* How BOSS Levels (Episodes 2 and 3) work.
The rule is this: when a boss dies, if there are no other bosses
like it alive on the level, the game ends. Since in this map there
are two types of bosses, two extra bosses have to be added outside
the map (deaf) so the game doesn't end early. In fact, this level
doesn't end even when all visible opponents are dead. */
/* Add a BOSS, so game won't end prematurely */
thing.x = -171;
thing.y = -863;
thing.angle = (rand()%NUM_FACES)*(TOTAL_DEGREES/NUM_FACES);
thing.type = TYPE_CYBERLORD;
thing.flags = THING_LEVEL12 | THING_LEVEL3 | THING_LEVEL4 | THING_DEAF;
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
/* Add another BOSS, so game won't end prematurely */
thing.x = -1296;
thing.y = 507;
thing.angle = (rand()%NUM_FACES)*(TOTAL_DEGREES/NUM_FACES);
thing.type = TYPE_SPIDERDEMON;
thing.flags = THING_LEVEL12 | THING_LEVEL3 | THING_LEVEL4 | THING_DEAF;
fwrite(&thing, sizeof(thing), 1, dest->wf);
numThingsAdded++;
}
/* Bump up 'entryIndex' */
if (dest->entryIndex == INVALID_ENTRY)
dest->entryIndex = 0;
else
dest->entryIndex++;
if (dest->entryIndex >= dest->dirNumEntries) return FALSE;
/* Copy data to 'dirCache' */
ptr = &dest->dirCache[dest->entryIndex*SIZEOF_WAD_DIR_ENTRY];
memcpy(ptr, &location, SIZEOF_WAD_DIR_LOCATION);
ptr += SIZEOF_WAD_DIR_LOCATION;
numThingsAdded *= sizeof(ThingRec);
memcpy(ptr, &numThingsAdded, SIZEOF_WAD_DIR_SIZE);
ptr += SIZEOF_WAD_DIR_SIZE;
memcpy(ptr, src->entryName, SIZEOF_WAD_DIR_NAME);
/* Release THING memory */
WadfileLumpClose(src);
WadfileGetNextDirInfo(src);
/* Write out all other sections of the map */
for (i=0; i<NUM_ENTRIES_PER_MAP-2; i++)
{
/* Copy lump from source to destination */
if (!WadfileLumpCopy(dest, src->entryName, src))
return FALSE;
/* Go to next entry in the directory */
WadfileGetNextDirInfo(src);
}
return TRUE;
}
/*---------------------------- Main routine ------------------------------*/
/*************************************************************************
* *
* main *
* *
* Desc: *
* This creates a PWAD file for every map in DOOM's Wadfile. *
* *
* Def: *
* int main(int argc, char * argv[]); *
* *
* Parm: *
* argc Number of parameters *
* argv List of parameters *
* *
* Retn: *
* TRUE if all maps were copied successfully, FALSE otherwise. *
* *
* Notes: *
* Suggestion: You can rename this routine and call it from another *
* module. *
* *
*************************************************************************/
int main(int argc, char * argv[])
{
int episode, map; /* Loop control variables */
int i;
JumbledMapRec jumbleMap[DOOM_LASTEPISODE*DOOM_LASTMAP];
char musicName[SIZEOF_WAD_DIR_NAME];
unsigned long seed; /* Pseudo-random number generator seed */
Wadfile wad; /* DOOM's IWAD file pointer */
Wadfile jumbleWad; /* JUMBLE.WAD Wadfile */
/* Print title */
fprintf(stdout, "DOOM WAD Utility: %s\n", PROG_FILENAME);
fprintf(stdout, "This program has no affiliation with ID Software.\n");
fprintf(stdout, "Use at your own discretion.\n");
fprintf(stdout, "Author: %s Date: %s\n\n\n", PROG_AUTHOR, __DATE__);
/* Check command line */
if (argc < 2)
{
fprintf(stderr, "Format: %s filename.wad [random seed]", PROG_FILENAME);
return EXIT_ERROR;
}
/* A one-time Wadfile initialization to set up memory management */
WadfileInitialize(wadMalloc, wadFree);
/* Open WAD file, aborting if error or if it is a PWAD file */
if (!WadfileOpen(&wad, argv[1], WANTIWAD))
{
printf("Sorry, %s is not an IWAD file.\nAdios!\n", argv[1]);
return EXIT_ERROR;
}
/* Tip: Echo the command line parameter(s) to the user */
printf("Processing WAD file: %s\n", argv[1]);
/* Make sure this is a registered version of DOOM by looking for E3M1 */
/* Only check if file is an IWAD (that is, it came from id Software.) */
if ((wad.flags & FLAG_WADFILE_IWAD) && (!WadfileSeekMap(&wad, 3, 1)))
{
printf("Cannot work with SHAREWARE DOOM. Buy DOOM and try again.\n");
return EXIT_ERROR;
}
/* Create a new jumble PWAD */
unlink(JUMBLE_NAME);
if (!WadfileCreate(&jumbleWad, JUMBLE_NAME, TYPE_PWAD, MAX_ENTRIES))
{
fprintf(stderr, "Error creating %s!\n", JUMBLE_NAME);
return FALSE;
}
/* Randomize by either the parameter on command line or by the
system clock */
if (argc > 2)
seed = atol(argv[2]);
else
seed = (unsigned long)time(0);
srand(seed);
printf("Using random seed: %lu.\n", seed);
/* Initialize jumbled map */
for (episode=0; episode<DOOM_LASTEPISODE; episode++)
for (map=0; map<DOOM_LASTMAP; map++)
{
jumbleMap[episode*DOOM_LASTMAP+map].touched = JUMBLEREC_UNTOUCHED;
/* Create the music name for this level--this is completely random;
some songs are used many times, some not at all. */
sprintf(jumbleMap[episode*DOOM_LASTMAP+map].musicName, "D_E%dM%d\0",
1+(rand() % DOOM_LASTEPISODE), 1+(rand() % DOOM_LASTMAP));
}
/* The BOSS levels must stay in the same place, or else they won't end. */
for (episode=0; episode<DOOM_LASTEPISODE; episode++)
{
jumbleMap[episode*DOOM_LASTMAP+DOOM_BOSSLEVEL-1].episode = episode+1;
jumbleMap[episode*DOOM_LASTMAP+DOOM_BOSSLEVEL-1].map = DOOM_BOSSLEVEL;
memcpy(jumbleMap[episode*DOOM_LASTMAP+DOOM_BOSSLEVEL-1].musicName,
bossEpisodeMusic[episode], SIZEOF_WAD_DIR_NAME);
jumbleMap[episode*DOOM_LASTMAP+DOOM_BOSSLEVEL-1].touched = JUMBLEREC_TOUCHED;
}
/* Create the jumble map */
for (episode=0; episode<DOOM_LASTEPISODE; episode++)
for (map=0; map<DOOM_LASTMAP; map++)
{
if (map != (DOOM_BOSSLEVEL-1))
{
do{
i = rand()%(DOOM_LASTEPISODE*DOOM_LASTMAP);
} while (jumbleMap[i].touched == JUMBLEREC_TOUCHED);
jumbleMap[i].touched = JUMBLEREC_TOUCHED;
jumbleMap[episode*DOOM_LASTMAP+map].episode = (i/DOOM_LASTMAP)+1;
jumbleMap[episode*DOOM_LASTMAP+map].map = (i%DOOM_LASTMAP)+1;
}
}
/* Tip: Give the user something to read */
printf("Creating %s\n", JUMBLE_NAME);
/* Do the jumbling one map at a time */
for (episode=0; episode<DOOM_LASTEPISODE; episode++)
for (map=0; map<DOOM_LASTMAP; map++)
{
/* Tip: Give the user something to read */
printf("Jumbling E%dM%d using a map from episode %d.\n",episode+1, map+1,
jumbleMap[episode*DOOM_LASTMAP+map].episode);
/* Create the music name for this level */
sprintf(musicName, "D_E%dM%d\0", episode+1, map+1);
/* Extract a map, jumble it, and write it to JUMBLE.WAD */
if ((!WadfileCopyAndJumbleMap(&jumbleWad, episode+1, map+1, &wad,
jumbleMap[episode*DOOM_LASTMAP+map].episode,
jumbleMap[episode*DOOM_LASTMAP+map].map)) ||
(!WadfileCopyEntry(&jumbleWad, musicName,
&wad, jumbleMap[episode*DOOM_LASTMAP+map].musicName)))
{
fprintf(stderr, "Error creating %s.\n", JUMBLE_NAME);
return EXIT_ERROR;
}
}
/* This reassures the user the process is ending successfully */
printf("Done!\n\n");
/* Close up shop */
WadfileClose(&jumbleWad);
WadfileClose(&wad);
printf("Now, run DOOM by typing:\n");
printf(" DOOM -file JUMBLE.WAD { other normal paramters }\n\n");
printf("See JUMBLE.DOC for more specific help.\n");
return EXIT_OK;
}