Index: src/Command.h ================================================================== --- src/Command.h +++ src/Command.h @@ -1,19 +1,29 @@ #import "Identifier.h" OF_ASSUME_NONNULL_BEGIN + +#define COMMAND(name, nargs, block_) \ + OF_CONSTRUCTOR() \ + { \ + enqueueInit(^{ \ + [Identifier \ + addIdentifier:[Command commandWithName:@ #name \ + argumentsTypes:nargs \ + block:block_]]; \ + }); \ + } @interface Command: Identifier -@property (readonly, nonatomic) void (*function)(); @property (readonly, nonatomic) int argumentsTypes; + (instancetype)commandWithName:(OFString *)name - function:(void (*)())function - argumentsTypes:(int)argumentsTypes; + argumentsTypes:(int)argumentsTypes + block:(id)block; - (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name - function:(void (*)())function - argumentsTypes:(int)argumentsTypes; + argumentsTypes:(int)argumentsTypes + block:(id)block; - (int)callWithArguments:(OFArray *)arguments isDown:(bool)isDown; @end OF_ASSUME_NONNULL_END Index: src/Command.m ================================================================== --- src/Command.m +++ src/Command.m @@ -18,27 +18,31 @@ [copy makeImmutable]; return copy; } @implementation Command +{ + id _block; +} + + (instancetype)commandWithName:(OFString *)name - function:(void (*)())function argumentsTypes:(int)argumentsTypes + block:(id)block { return [[self alloc] initWithName:name - function:function - argumentsTypes:argumentsTypes]; + argumentsTypes:argumentsTypes + block:block]; } - (instancetype)initWithName:(OFString *)name - function:(void (*)())function argumentsTypes:(int)argumentsTypes + block:(id)block { self = [super initWithName:name]; - _function = function; _argumentsTypes = argumentsTypes; + _block = block; return self; } - (int)callWithArguments:(OFArray *)arguments isDown:(bool)isDown @@ -45,119 +49,116 @@ { switch (_argumentsTypes) { case ARG_1INT: if (isDown) { arguments = padArguments(arguments, 2); - ((void(__cdecl *)(int))_function)( + ((void (^)(int))_block)( [arguments[1] cube_intValueWithBase:0]); } break; case ARG_2INT: if (isDown) { arguments = padArguments(arguments, 3); - ((void(__cdecl *)(int, int))_function)( + ((void (^)(int, int))_block)( [arguments[1] cube_intValueWithBase:0], [arguments[2] cube_intValueWithBase:0]); } break; case ARG_3INT: if (isDown) { arguments = padArguments(arguments, 4); - ((void(__cdecl *)(int, int, int))_function)( + ((void (^)(int, int, int))_block)( [arguments[1] cube_intValueWithBase:0], [arguments[2] cube_intValueWithBase:0], [arguments[3] cube_intValueWithBase:0]); } break; case ARG_4INT: if (isDown) { arguments = padArguments(arguments, 5); - ((void(__cdecl *)(int, int, int, int))_function)( + ((void (^)(int, int, int, int))_block)( [arguments[1] cube_intValueWithBase:0], [arguments[2] cube_intValueWithBase:0], [arguments[3] cube_intValueWithBase:0], [arguments[4] cube_intValueWithBase:0]); } break; case ARG_NONE: if (isDown) - ((void(__cdecl *)())_function)(); + ((void (^)())_block)(); break; case ARG_1STR: if (isDown) { arguments = padArguments(arguments, 2); - ((void(__cdecl *)(OFString *))_function)(arguments[1]); + ((void (^)(OFString *))_block)(arguments[1]); } break; case ARG_2STR: if (isDown) { arguments = padArguments(arguments, 3); - ((void(__cdecl *)(OFString *, OFString *))_function)( + ((void (^)(OFString *, OFString *))_block)( arguments[1], arguments[2]); } break; case ARG_3STR: if (isDown) { arguments = padArguments(arguments, 4); - ((void(__cdecl *)( - OFString *, OFString *, OFString *))_function)( + ((void (^)(OFString *, OFString *, OFString *))_block)( arguments[1], arguments[2], arguments[3]); } break; case ARG_5STR: if (isDown) { arguments = padArguments(arguments, 6); - ((void(__cdecl *)(OFString *, OFString *, OFString *, - OFString *, OFString *))_function)(arguments[1], + ((void (^)(OFString *, OFString *, OFString *, + OFString *, OFString *))_block)(arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); } break; case ARG_DOWN: - ((void(__cdecl *)(bool))_function)(isDown); + ((void (^)(bool))_block)(isDown); break; case ARG_DWN1: arguments = padArguments(arguments, 2); - ((void(__cdecl *)(bool, OFString *))_function)( - isDown, arguments[1]); + ((void (^)(bool, OFString *))_block)(isDown, arguments[1]); break; case ARG_1EXP: if (isDown) { arguments = padArguments(arguments, 2); - return ((int(__cdecl *)(int))_function)( + return ((int (^)(int))_block)( execute(arguments[1], isDown)); } break; case ARG_2EXP: if (isDown) { arguments = padArguments(arguments, 3); - return ((int(__cdecl *)(int, int))_function)( + return ((int (^)(int, int))_block)( execute(arguments[1], isDown), execute(arguments[2], isDown)); } break; case ARG_1EST: if (isDown) { arguments = padArguments(arguments, 2); - return ((int(__cdecl *)(OFString *))_function)( - arguments[1]); + return ((int (^)(OFString *))_block)(arguments[1]); } break; case ARG_2EST: if (isDown) { arguments = padArguments(arguments, 3); - return ((int(__cdecl *)(OFString *, - OFString *))_function)(arguments[1], arguments[2]); + return ((int (^)(OFString *, OFString *))_block)( + arguments[1], arguments[2]); } break; case ARG_VARI: if (isDown) // limit, remove - ((void(__cdecl *)(OFString *))_function)([[arguments + ((void (^)(OFString *))_block)([[arguments objectsInRange:OFMakeRange(1, arguments.count - 1)] componentsJoinedByString:@" "]); break; } return 0; } @end Index: src/Cube.m ================================================================== --- src/Cube.m +++ src/Cube.m @@ -1,9 +1,10 @@ // main.cpp: initialisation & main loop #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" OF_APPLICATION_DELEGATE(Cube) VARF(gamespeed, 10, 100, 1000, if (multiplayer()) gamespeed = 100); @@ -371,18 +372,13 @@ [Cube.sharedInstance showMessage:msg]; [OFApplication terminateWithStatus:1]; } -void -quit() // normal exit -{ - [Cube.sharedInstance quit]; -} -COMMAND(quit, ARG_NONE) - -void -screenshot() -{ - [Cube.sharedInstance screenshot]; -} -COMMAND(screenshot, ARG_NONE) +// normal exit +COMMAND(quit, ARG_NONE, ^{ + [Cube.sharedInstance quit]; +}) + +COMMAND(screenshot, ARG_NONE, ^{ + [Cube.sharedInstance screenshot]; +}) Index: src/Identifier.h ================================================================== --- src/Identifier.h +++ src/Identifier.h @@ -3,10 +3,13 @@ OF_ASSUME_NONNULL_BEGIN @interface Identifier: OFObject @property (readonly, copy, nonatomic) OFString *name; ++ (void)addIdentifier:(__kindof Identifier *)identifier; ++ (__kindof Identifier *)identifierForName:(OFString *)name; ++ (void)enumerateIdentifiersUsingBlock:(void (^)(__kindof Identifier *))block; - (instancetype)init OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name; @end OF_ASSUME_NONNULL_END Index: src/Identifier.m ================================================================== --- src/Identifier.m +++ src/Identifier.m @@ -1,8 +1,35 @@ #import "Identifier.h" +// contains ALL vars/commands/aliases +static OFMutableDictionary *identifiers; + @implementation Identifier ++ (void)initialize +{ + if (self == Identifier.class) + identifiers = [[OFMutableDictionary alloc] init]; +} + ++ (void)addIdentifier:(__kindof Identifier *)identifier +{ + identifiers[identifier.name] = identifier; +} + ++ (__kindof Identifier *)identifierForName:(OFString *)name +{ + return identifiers[name]; +} + ++ (void)enumerateIdentifiersUsingBlock:(void (^)(__kindof Identifier *))block +{ + [identifiers enumerateKeysAndObjectsUsingBlock:^( + OFString *name, __kindof Identifier *identifier, bool *stop) { + block(identifier); + }]; +} + - (instancetype)initWithName:(OFString *)name { self = [super init]; _name = [name copy]; Index: src/clientextras.m ================================================================== --- src/clientextras.m +++ src/clientextras.m @@ -1,9 +1,10 @@ // clientextras.cpp: stuff that didn't fit in client.cpp or clientgame.cpp :) #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Monster.h" // render players & monsters // very messy ad-hoc handling of animation frames, should be made more @@ -175,13 +176,11 @@ } } // sendmap/getmap commands, should be replaced by more intuitive map downloading -void -sendmap(OFString *mapname) -{ +COMMAND(sendmap, ARG_1STR, (^(OFString *mapname) { if (mapname.length > 0) save_world(mapname); changemap(mapname); mapname = getclientmap(); OFData *mapdata = readmap(mapname); @@ -208,23 +207,18 @@ OFString *msg = [OFString stringWithFormat:@"[map %@ uploaded to server, " @"\"getmap\" to receive it]", mapname]; toserver(msg); -} +})) -void -getmap() -{ +COMMAND(getmap, ARG_NONE, ^{ ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); unsigned char *start = packet->data; unsigned char *p = start + 2; putint(&p, SV_RECVMAP); *(unsigned short *)start = ENET_HOST_TO_NET_16(p - start); enet_packet_resize(packet, p - start); sendpackettoserv(packet); conoutf(@"requesting map from server..."); -} - -COMMAND(sendmap, ARG_1STR) -COMMAND(getmap, ARG_NONE) +}) Index: src/clientgame.m ================================================================== --- src/clientgame.m +++ src/clientgame.m @@ -1,23 +1,21 @@ // clientgame.cpp: core game related stuff #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Entity.h" #import "Monster.h" #import "OFString+Cube.h" int nextmode = 0; // nextmode becomes gamemode after next map load VAR(gamemode, 1, 0, 0); -static void -mode(int n) -{ +COMMAND(mode, ARG_1INT, ^(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); -} -COMMAND(mode, ARG_1INT) +}) bool intermission = false; DynamicEntity *player1; // our client OFMutableArray *players; // other clients @@ -140,17 +138,14 @@ } } int sleepwait = 0; static OFString *sleepcmd = nil; -void -sleepf(OFString *msec, OFString *cmd) -{ +COMMAND(sleep, ARG_2STR, ^(OFString *msec, OFString *cmd) { sleepwait = msec.cube_intValue + lastmillis; sleepcmd = cmd; -} -COMMANDN(sleep, sleepf, ARG_2STR) +}) void updateworld(int millis) // main game update loop { if (lastmillis) { @@ -238,47 +233,38 @@ } // movement input code #define dir(name, v, d, s, os) \ - static void name(bool isdown) \ - { \ - player1.s = isdown; \ - player1.v = isdown ? d : (player1.os ? -(d) : 0); \ + COMMAND(name, ARG_DOWN, ^(bool isDown) { \ + player1.s = isDown; \ + player1.v = isDown ? d : (player1.os ? -(d) : 0); \ player1.lastMove = lastmillis; \ - } + }) dir(backward, move, -1, k_down, k_up); dir(forward, move, 1, k_up, k_down); dir(left, strafe, 1, k_left, k_right); dir(right, strafe, -1, k_right, k_left); -void -attack(bool on) -{ +COMMAND(attack, ARG_DOWN, ^(bool on) { if (intermission) return; if (editmode) editdrag(on); else if ((player1.attacking = on)) respawn(); -} +}) -void -jumpn(bool on) -{ +COMMAND(jump, ARG_DOWN, ^(bool on) { if (!intermission && (player1.jumpNext = on)) respawn(); -} - -COMMAND(backward, ARG_DOWN) -COMMAND(forward, ARG_DOWN) -COMMAND(left, ARG_DOWN) -COMMAND(right, ARG_DOWN) -COMMANDN(jump, jumpn, ARG_DOWN) -COMMAND(attack, ARG_DOWN) -COMMAND(showscores, ARG_DOWN) +}) + +COMMAND(showscores, ARG_DOWN, ^(bool isDown) { + showscores(isDown); +}) void fixplayer1range() { const float MAXPITCH = 90.0f; @@ -438,6 +424,8 @@ intermission = false; Cube.sharedInstance.framesInMap = 0; conoutf(@"game mode is %@", modestr(gamemode)); } -COMMANDN(map, changemap, ARG_1STR) +COMMAND(map, ARG_1STR, ^(OFString *name) { + changemap(name); +}) Index: src/clients.m ================================================================== --- src/clients.m +++ src/clients.m @@ -1,9 +1,10 @@ // client.cpp, mostly network related client game code #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" static ENetHost *clienthost = NULL; static int connecting = 0; static int connattempts = 0; @@ -58,33 +59,39 @@ assert(ENET_PEER_PACKET_THROTTLE_SCALE == 32); enet_peer_throttle_configure(clienthost->peers, throttle_interval * 1000, throttle_accel, throttle_decel); } -void +static void newname(OFString *name) { c2sinit = false; if (name.length > 16) name = [name substringToIndex:16]; player1.name = name; } -COMMANDN(name, newname, ARG_1STR) + +COMMAND(name, ARG_1STR, ^(OFString *name) { + newname(name); +}) -void +static void newteam(OFString *name) { c2sinit = false; if (name.length > 5) name = [name substringToIndex:5]; player1.team = name; } -COMMANDN(team, newteam, ARG_1STR) + +COMMAND(team, ARG_1STR, ^(OFString *name) { + newteam(name); +}) void writeclientinfo(OFStream *stream) { [stream writeFormat:@"name \"%@\"\nteam \"%@\"\n", player1.name, @@ -175,20 +182,22 @@ { conoutf(@"%@:\f %@", player1.name, text); ctext = text; } -void -echo(OFString *text) -{ +COMMAND(echo, ARG_VARI, ^(OFString *text) { conoutf(@"%@", text); -} - -COMMAND(echo, ARG_VARI) -COMMANDN(say, toserver, ARG_VARI) -COMMANDN(connect, connects, ARG_1STR) -COMMANDN(disconnect, trydisconnect, ARG_NONE) +}) +COMMAND(say, ARG_VARI, ^(OFString *text) { + toserver(text); +}) +COMMAND(connect, ARG_1STR, ^(OFString *servername) { + connects(servername); +}) +COMMAND(disconnect, ARG_NONE, ^{ + trydisconnect(); +}) // collect c2s messages conveniently static OFMutableArray *messages; @@ -237,16 +246,13 @@ OFString *toservermap; bool senditemstoserver = false; // after a map change, since server doesn't have map data OFString *clientpassword; -void -password(OFString *p) -{ +COMMAND(password, ARG_1STR, ^(OFString *p) { clientpassword = p; -} -COMMAND(password, ARG_1STR) +}) bool netmapstart() { senditemstoserver = true; Index: src/commands.m ================================================================== --- src/commands.m +++ src/commands.m @@ -7,104 +7,88 @@ #import "Command.h" #import "Identifier.h" #import "OFString+Cube.h" #import "Variable.h" -// contains ALL vars/commands/aliases -static OFMutableDictionary *identifiers; - static void cleanup(char **string) { free(*string); } void alias(OFString *name, OFString *action) { - Alias *alias = identifiers[name]; - - if (alias == nil) { - alias = [Alias aliasWithName:name action:action persisted:true]; - - if (identifiers == nil) - identifiers = [[OFMutableDictionary alloc] init]; - - identifiers[name] = alias; - } else { + Alias *alias = [Identifier identifierForName:name]; + + if (alias == nil) + [Identifier addIdentifier:[Alias aliasWithName:name + action:action + persisted:true]]; + else { if ([alias isKindOfClass:Alias.class]) alias.action = action; else conoutf( @"cannot redefine builtin %@ with an alias", name); } } -COMMAND(alias, ARG_2STR) + +COMMAND(alias, ARG_2STR, ^(OFString *name, OFString *action) { + alias(name, action); +}) int variable(OFString *name, int min, int cur, int max, int *storage, void (*function)(), bool persisted) { - Variable *variable = [Variable variableWithName:name - min:min - max:max - storage:storage - function:function - persisted:persisted]; - - if (identifiers == nil) - identifiers = [[OFMutableDictionary alloc] init]; - - identifiers[name] = variable; - + [Identifier addIdentifier:[Variable variableWithName:name + min:min + max:max + storage:storage + function:function + persisted:persisted]]; return cur; } void setvar(OFString *name, int i) { - *[identifiers[name] storage] = i; + Variable *variable = [Identifier identifierForName:name]; + + if ([variable isKindOfClass:Variable.class]) + *variable.storage = i; } int getvar(OFString *name) { - return *[identifiers[name] storage]; + Variable *variable = [Identifier identifierForName:name]; + + if ([variable isKindOfClass:Variable.class]) + return *variable.storage; + + return 0; } bool identexists(OFString *name) { - return (identifiers[name] != nil); + return ([Identifier identifierForName:name] != nil); } OFString * getalias(OFString *name) { - Alias *alias = identifiers[name]; + Alias *alias = [Identifier identifierForName:name]; if ([alias isKindOfClass:Alias.class]) return alias.action; return nil; } -bool -addcommand(OFString *name, void (*function)(), int argumentsTypes) -{ - Command *command = [Command commandWithName:name - function:function - argumentsTypes:argumentsTypes]; - - if (identifiers == nil) - identifiers = [[OFMutableDictionary alloc] init]; - - identifiers[name] = command; - - return false; -} - // parse any nested set of () or [] static char * parseexp(char **p, int right) { int left = *(*p)++; @@ -166,11 +150,12 @@ // find value of ident referenced with $ in exp OFString * lookup(OFString *n) { - __kindof Identifier *identifier = identifiers[[n substringFromIndex:1]]; + __kindof Identifier *identifier = + [Identifier identifierForName:[n substringFromIndex:1]]; if ([identifier isKindOfClass:Variable.class]) { return [OFString stringWithFormat:@"%d", *[identifier storage]]; } else if ([identifier isKindOfClass:Alias.class]) return [identifier action]; @@ -273,11 +258,11 @@ } // empty statement if (c.length == 0) continue; - val = executeIdentifier(identifiers[c], + val = executeIdentifier([Identifier identifierForName:c], [OFArray arrayWithObjects:w count:numargs], isDown); } return val; } @@ -305,12 +290,12 @@ completesize = s.length - 1; completeidx = 0; } __block int idx = 0; - [identifiers enumerateKeysAndObjectsUsingBlock:^( - OFString *name, Identifier *identifier, bool *stop) { + [Identifier enumerateIdentifiersUsingBlock:^( + __kindof Identifier *identifier) { if (strncmp(identifier.name.UTF8String, s.UTF8String + 1, completesize) == 0 && idx++ == completeidx) [s replaceCharactersInRange:OFMakeRange(1, s.length - 1) withString:identifier.name]; @@ -345,10 +330,14 @@ IRIByAppendingPathComponent:cfgfile]) && !execfile([Cube.sharedInstance.gameDataIRI IRIByAppendingPathComponent:cfgfile])) conoutf(@"could not read \"%@\"", cfgfile); } + +COMMAND(exec, ARG_1STR, ^(OFString *cfgfile) { + exec(cfgfile); +}) void writecfg() { OFStream *stream; @@ -368,38 +357,40 @@ @"autoexec.cfg to override anything\n" @"\n"]; writeclientinfo(stream); [stream writeString:@"\n"]; - [identifiers enumerateKeysAndObjectsUsingBlock:^( - OFString *name, __kindof Identifier *identifier, bool *stop) { - if (![identifier isKindOfClass:Variable.class] || - ![identifier persisted]) - return; - - [stream writeFormat:@"%@ %d\n", identifier.name, - *[identifier storage]]; - }]; + [Identifier + enumerateIdentifiersUsingBlock:^(__kindof Identifier *identifier) { + if (![identifier isKindOfClass:Variable.class] || + ![identifier persisted]) + return; + + [stream writeFormat:@"%@ %d\n", identifier.name, + *[identifier storage]]; + }]; [stream writeString:@"\n"]; writebinds(stream); [stream writeString:@"\n"]; - [identifiers enumerateKeysAndObjectsUsingBlock:^( - OFString *name, __kindof Identifier *identifier, bool *stop) { - if (![identifier isKindOfClass:Alias.class] || - [identifier.name hasPrefix:@"nextmap_"]) - return; - - [stream writeFormat:@"alias \"%@\" [%@]\n", identifier.name, - [identifier action]]; - }]; + [Identifier + enumerateIdentifiersUsingBlock:^(__kindof Identifier *identifier) { + if (![identifier isKindOfClass:Alias.class] || + [identifier.name hasPrefix:@"nextmap_"]) + return; + + [stream writeFormat:@"alias \"%@\" [%@]\n", identifier.name, + [identifier action]]; + }]; [stream close]; } -COMMAND(writecfg, ARG_NONE) +COMMAND(writecfg, ARG_NONE, ^{ + writecfg(); +}) // below the commands that implement a small imperative language. thanks to the // semantics of () and [] expressions, any control construct can be defined // trivially. @@ -407,56 +398,48 @@ intset(OFString *name, int v) { alias(name, [OFString stringWithFormat:@"%d", v]); } -void -ifthen(OFString *cond, OFString *thenp, OFString *elsep) -{ +COMMAND(if, ARG_3STR, ^(OFString *cond, OFString *thenp, OFString *elsep) { execute((![cond hasPrefix:@"0"] ? thenp : elsep), true); -} +}) -void -loopa(OFString *times, OFString *body) -{ +COMMAND(loop, ARG_2STR, ^(OFString *times, OFString *body) { int t = times.cube_intValue; for (int i = 0; i < t; i++) { intset(@"i", i); execute(body, true); } -} +}) -void -whilea(OFString *cond, OFString *body) -{ +COMMAND(while, ARG_2STR, ^(OFString *cond, OFString *body) { while (execute(cond, true)) execute(body, true); -} +}) -void -onrelease(bool on, OFString *body) -{ +COMMAND(onrelease, ARG_DWN1, ^(bool on, OFString *body) { if (!on) execute(body, true); -} +}) void concat(OFString *s) { alias(@"s", s); } -void -concatword(OFString *s) -{ +COMMAND(concat, ARG_VARI, ^(OFString *s) { + concat(s); +}) + +COMMAND(concatword, ARG_VARI, ^(OFString *s) { concat([s stringByReplacingOccurrencesOfString:@" " withString:@""]); -} +}) -int -listlen(OFString *a_) -{ +COMMAND(listlen, ARG_1EST, ^(OFString *a_) { const char *a = a_.UTF8String; if (!*a) return 0; @@ -464,15 +447,13 @@ while (*a) if (*a++ == ' ') n++; return n + 1; -} +}) -void -at(OFString *s_, OFString *pos) -{ +COMMAND(at, ARG_2STR, ^(OFString *s_, OFString *pos) { int n = pos.cube_intValue; char *copy __attribute__((__cleanup__(cleanup))) = strdup(s_.UTF8String); char *s = copy; for (int i = 0; i < n; i++) { @@ -479,93 +460,50 @@ s += strcspn(s, " \0"); s += strspn(s, " "); } s[strcspn(s, " \0")] = 0; concat(@(s)); -} - -COMMANDN(loop, loopa, ARG_2STR) -COMMANDN(while, whilea, ARG_2STR) -COMMANDN(if, ifthen, ARG_3STR) -COMMAND(onrelease, ARG_DWN1) -COMMAND(exec, ARG_1STR) -COMMAND(concat, ARG_VARI) -COMMAND(concatword, ARG_VARI) -COMMAND(at, ARG_2STR) -COMMAND(listlen, ARG_1EST) - -int -add(int a, int b) -{ - return a + b; -} -COMMANDN(+, add, ARG_2EXP) - -int -mul(int a, int b) -{ - return a * b; -} -COMMANDN(*, mul, ARG_2EXP) - -int -sub(int a, int b) -{ - return a - b; -} -COMMANDN(-, sub, ARG_2EXP) - -int -divi(int a, int b) -{ - return b ? a / b : 0; -} -COMMANDN(div, divi, ARG_2EXP) - -int -mod(int a, int b) -{ - return b ? a % b : 0; -} -COMMAND(mod, ARG_2EXP) - -int -equal(int a, int b) -{ - return (int)(a == b); -} -COMMANDN(=, equal, ARG_2EXP) - -int -lt(int a, int b) -{ - return (int)(a < b); -} -COMMANDN(<, lt, ARG_2EXP) - -int -gt(int a, int b) -{ - return (int)(a > b); -} -COMMANDN(>, gt, ARG_2EXP) - -int -strcmpa(OFString *a, OFString *b) -{ - return [a isEqual:b]; -} -COMMANDN(strcmp, strcmpa, ARG_2EST) - -int -rndn(int a) -{ - return a > 0 ? rnd(a) : 0; -} -COMMANDN(rnd, rndn, ARG_1EXP) - -int -explastmillis() -{ - return lastmillis; -} -COMMANDN(millis, explastmillis, ARG_1EXP) +}) + +COMMAND(+, ARG_2EXP, ^(int a, int b) { + return a + b; +}) + +COMMAND(*, ARG_2EXP, ^(int a, int b) { + return a * b; +}) + +COMMAND(-, ARG_2EXP, ^(int a, int b) { + return a - b; +}) + +COMMAND(div, ARG_2EXP, ^(int a, int b) { + return b ? a / b : 0; +}) + +COMMAND(mod, ARG_2EXP, ^(int a, int b) { + return b ? a % b : 0; +}) + +COMMAND(=, ARG_2EXP, ^(int a, int b) { + return (int)(a == b); +}) + +COMMAND(<, ARG_2EXP, ^(int a, int b) { + return (int)(a < b); +}) + +COMMAND(>, ARG_2EXP, ^(int a, int b) { + return (int)(a > b); +}) + +COMMAND(strcmp, ARG_2EST, ^(OFString *a, OFString *b) { + return [a isEqual:b]; +}) + +COMMAND(rnd, ARG_1EXP, ^(int a) { + return (a > 0 ? rnd(a) : 0); +}) + +COMMAND(millis, ARG_1EXP, ^(int unused) { + return lastmillis; +}) Index: src/console.m ================================================================== --- src/console.m +++ src/console.m @@ -2,10 +2,11 @@ #include "cube.h" #include +#import "Command.h" #import "ConsoleLine.h" #import "KeyMapping.h" #import "OFString+Cube.h" static OFMutableArray *conlines; @@ -15,18 +16,15 @@ int conskip = 0; bool saycommandon = false; static OFMutableString *commandbuf; -void -setconskip(int n) -{ +COMMAND(conskip, ARG_1INT, ^(int n) { conskip += n; if (conskip < 0) conskip = 0; -} -COMMANDN(conskip, setconskip, ARG_1INT) +}) static void conline(OFString *sf, bool highlight) // add a line to the console buffer { OFMutableString *text; @@ -102,40 +100,35 @@ // keymap is defined externally in keymap.cfg static OFMutableArray *keyMappings = nil; -void -keymap(OFString *code, OFString *key, OFString *action) -{ +COMMAND(keymap, ARG_3STR, ^(OFString *code, OFString *key, OFString *action) { if (keyMappings == nil) keyMappings = [[OFMutableArray alloc] init]; KeyMapping *mapping = [KeyMapping mappingWithCode:code.cube_intValue name:key]; mapping.action = action; [keyMappings addObject:mapping]; -} -COMMAND(keymap, ARG_3STR) +}) -void -bindkey(OFString *key, OFString *action) -{ +COMMAND(bind, ARG_2STR, ^(OFString *key, OFString *action) { for (KeyMapping *mapping in keyMappings) { if ([mapping.name caseInsensitiveCompare:key] == OFOrderedSame) { mapping.action = action; return; } } conoutf(@"unknown key \"%@\"", key); -} -COMMANDN(bind, bindkey, ARG_2STR) +}) -void -saycommand(OFString *init) // turns input to the command line on or off +// turns input to the command line on or off +static void +saycommand(OFString *init) { saycommandon = (init != nil); if (saycommandon) SDL_StartTextInput(); else @@ -147,19 +140,19 @@ if (init == nil) init = @""; commandbuf = [init mutableCopy]; } -COMMAND(saycommand, ARG_VARI) + +COMMAND(saycommand, ARG_VARI, ^(OFString *init) { + saycommand(init); +}) -void -mapmsg(OFString *s) -{ +COMMAND(mapmsg, ARG_1STR, ^(OFString *s) { memset(hdr.maptitle, '\0', sizeof(hdr.maptitle)); strncpy(hdr.maptitle, s.UTF8String, 127); -} -COMMAND(mapmsg, ARG_1STR) +}) void pasteconsole() { [commandbuf appendString:@(SDL_GetClipboardText())]; @@ -166,22 +159,19 @@ } static OFMutableArray *vhistory; static int histpos = 0; -void -history(int n) -{ +COMMAND(history, ARG_1INT, ^(int n) { static bool rec = false; if (!rec && n >= 0 && n < vhistory.count) { rec = true; execute(vhistory[vhistory.count - n - 1], true); rec = false; } -} -COMMAND(history, ARG_1INT) +}) void keypress(int code, bool isDown) { // keystrokes go to commandline Index: src/cube.h ================================================================== --- src/cube.h +++ src/cube.h @@ -338,18 +338,10 @@ ARG_VARI }; // nasty macros for registering script functions, abuses globals to avoid // excessive infrastructure -#define COMMANDN(name, fun, nargs) \ - OF_CONSTRUCTOR() \ - { \ - enqueueInit(^{ \ - addcommand(@ #name, (void (*)())fun, nargs); \ - }); \ - } -#define COMMAND(name, nargs) COMMANDN(name, name, nargs) #define VARP(name, min, cur, max) \ int name; \ OF_CONSTRUCTOR() \ { \ enqueueInit(^{ \ Index: src/editing.m ================================================================== --- src/editing.m +++ src/editing.m @@ -1,10 +1,11 @@ // editing.cpp: most map editing commands go here, entity editing commands are // in world.cpp #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Monster.h" #import "OFString+Cube.h" bool editmode = false; @@ -76,11 +77,14 @@ } Cube.sharedInstance.repeatsKeys = editmode; selset = false; editing = editmode; } -COMMANDN(edittoggle, toggleedit, ARG_NONE) + +COMMAND(edittoggle, ARG_NONE, ^{ + toggleedit(); +}) void correctsel() // ensures above invariant { selset = !OUTBORD(sel.x, sel.y); @@ -118,18 +122,16 @@ return; #define EDITMP \ if (noteditmode() || multiplayer()) \ return; -void -selectpos(int x, int y, int xs, int ys) -{ +COMMAND(select, ARG_4INT, (^(int x, int y, int xs, int ys) { struct block s = { x, y, xs, ys }; sel = s; selh = 0; correctsel(); -} +})) void makesel() { struct block s = { min(lastx, cx), min(lasty, cy), abs(lastx - cx) + 1, @@ -270,40 +272,34 @@ struct block *copy = blockcopy(&sel); [undos addItem:©]; pruneundos(undomegs << 20); } -void -editundo() -{ +COMMAND(undo, ARG_NONE, ^{ EDITMP; if (undos.count == 0) { conoutf(@"nothing more to undo"); return; } struct block *p = undos.mutableLastItem; [undos removeLastItem]; blockpaste(p); OFFreeMemory(p); -} +}) static struct block *copybuf = NULL; -void -copy() -{ +COMMAND(copy, ARG_NONE, ^{ EDITSELMP; if (copybuf) OFFreeMemory(copybuf); copybuf = blockcopy(&sel); -} +}) -void -paste() -{ +COMMAND(paste, ARG_NONE, ^{ EDITMP; if (!copybuf) { conoutf(@"nothing to paste"); return; } @@ -316,11 +312,11 @@ } makeundo(); copybuf->x = sel.x; copybuf->y = sel.y; blockpaste(copybuf); -} +}) void tofronttex() // maintain most recently used of the texture lists when applying // texture { @@ -376,11 +372,14 @@ bool isfloor = flr == 0; editheightxy(isfloor, amount, &sel); addmsg(1, 7, SV_EDITH, sel.x, sel.y, sel.xs, sel.ys, isfloor, amount); } -COMMAND(editheight, ARG_2INT) + +COMMAND(editheight, ARG_2INT, ^(int flr, int amount) { + editheight(flr, amount); +}) void edittexxy(int type, int t, const struct block *sel) { loopselxy(switch (type) { @@ -397,13 +396,11 @@ s->utex = t; break; }); } -void -edittex(int type, int dir) -{ +COMMAND(edittex, ARG_2INT, ^(int type, int dir) { EDITSEL; if (type < 0 || type > 3) return; @@ -417,15 +414,13 @@ i = i < 0 ? 0 : i + dir; curedittex[atype] = i = min(max(i, 0), 255); int t = lasttex = hdr.texlists[atype][i]; edittexxy(type, t, &sel); addmsg(1, 7, SV_EDITT, sel.x, sel.y, sel.xs, sel.ys, type, t); -} +}) -void -replace() -{ +COMMAND(replace, ARG_NONE, (^{ EDITSELMP; for (int x = 0; x < ssize; x++) { for (int y = 0; y < ssize; y++) { struct sqr *s = S(x, y); @@ -450,19 +445,19 @@ } } struct block b = { 0, 0, ssize, ssize }; remip(&b, 0); -} +})) void edittypexy(int type, const struct block *sel) { loopselxy(s->type = type); } -void +static void edittype(int type) { EDITSEL; if (type == CORNER && @@ -474,30 +469,21 @@ edittypexy(type, &sel); addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type); } -void -heightfield(int t) -{ +COMMAND(heightfield, ARG_1INT, ^(int t) { edittype(t == 0 ? FHF : CHF); -} -COMMAND(heightfield, ARG_1INT) +}) -void -solid(int t) -{ +COMMAND(solid, ARG_1INT, ^(int t) { edittype(t == 0 ? SPACE : SOLID); -} -COMMAND(solid, ARG_1INT) +}) -void -corner() -{ +COMMAND(corner, ARG_NONE, ^{ edittype(CORNER); -} -COMMAND(corner, ARG_NONE) +}) void editequalisexy(bool isfloor, const struct block *sel) { int low = 127, hi = -128; @@ -515,59 +501,50 @@ if (s->floor >= s->ceil) s->floor = s->ceil - 1; }); } -void -equalize(int flr) -{ +COMMAND(equalize, ARG_1INT, ^(int flr) { bool isfloor = (flr == 0); EDITSEL; editequalisexy(isfloor, &sel); addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor); -} -COMMAND(equalize, ARG_1INT) +}) void setvdeltaxy(int delta, const struct block *sel) { loopselxy(s->vdelta = max(s->vdelta + delta, 0)); remipmore(sel, 0); } -void -setvdelta(int delta) -{ +COMMAND(vdelta, ARG_1INT, ^(int delta) { EDITSEL; setvdeltaxy(delta, &sel); addmsg(1, 6, SV_EDITD, sel.x, sel.y, sel.xs, sel.ys, delta); -} +}) #define MAXARCHVERT 50 int archverts[MAXARCHVERT][MAXARCHVERT]; bool archvinit = false; -void -archvertex(int span, int vert, int delta) -{ +COMMAND(archvertex, ARG_3INT, ^(int span, int vert, int delta) { if (!archvinit) { archvinit = true; for (int s = 0; s < MAXARCHVERT; s++) for (int v = 0; v < MAXARCHVERT; v++) archverts[s][v] = 0; } if (span >= MAXARCHVERT || vert >= MAXARCHVERT || span < 0 || vert < 0) return; archverts[span][vert] = delta; -} +}) -void -arch(int sidedelta, int _a) -{ +COMMAND(arch, ARG_2INT, ^(int sidedelta, int _a) { EDITSELMP; sel.xs++; sel.ys++; @@ -583,15 +560,13 @@ ? (archverts[sel->xs - 1][x] + (y == 0 || y == sel->ys - 1 ? sidedelta : 0)) : (archverts[sel->ys - 1][y] + (x == 0 || x == sel->xs - 1 ? sidedelta : 0))); remipmore(sel, 0); -} +}) -void -slope(int xd, int yd) -{ +COMMAND(slope, ARG_2INT, ^(int xd, int yd) { EDITSELMP; int off = 0; if (xd < 0) off -= xd * sel.xs; @@ -604,15 +579,13 @@ struct block *sel_ = &sel; // Ugly hack to make the macro work. struct block *sel = sel_; loopselxy(s->vdelta = xd * x + yd * y + off); remipmore(sel, 0); -} +}) -void -perlin(int scale, int seed, int psize) -{ +COMMAND(perlin, ARG_3INT, ^(int scale, int seed, int psize) { EDITSELMP; sel.xs++; sel.ys++; @@ -628,49 +601,32 @@ remipmore(&sel, 0); sel.xs--; sel.ys--; -} +}) VARF( fullbright, 0, 0, 1, if (fullbright) { if (noteditmode()) return; for (int i = 0; i < mipsize; i++) world[i].r = world[i].g = world[i].b = 176; }); -void -edittag(int tag) -{ +COMMAND(edittag, ARG_1INT, ^(int tag) { EDITSELMP; struct block *sel_ = &sel; // Ugly hack to make the macro work. struct block *sel = sel_; loopselxy(s->tag = tag); -} - -void -newent(OFString *what, OFString *a1, OFString *a2, OFString *a3, OFString *a4) -{ - EDITSEL; - - newentity(sel.x, sel.y, (int)player1.origin.z, what, - [a1 cube_intValueWithBase:0], [a2 cube_intValueWithBase:0], - [a3 cube_intValueWithBase:0], [a4 cube_intValueWithBase:0]); -} - -COMMANDN(select, selectpos, ARG_4INT) -COMMAND(edittag, ARG_1INT) -COMMAND(replace, ARG_NONE) -COMMAND(archvertex, ARG_3INT) -COMMAND(arch, ARG_2INT) -COMMAND(slope, ARG_2INT) -COMMANDN(vdelta, setvdelta, ARG_1INT) -COMMANDN(undo, editundo, ARG_NONE) -COMMAND(copy, ARG_NONE) -COMMAND(paste, ARG_NONE) -COMMAND(edittex, ARG_2INT) -COMMAND(newent, ARG_5STR) -COMMAND(perlin, ARG_3INT) +}) + +COMMAND(newent, ARG_5STR, + ^(OFString *what, OFString *a1, OFString *a2, OFString *a3, OFString *a4) { + EDITSEL; + + newentity(sel.x, sel.y, (int)player1.origin.z, what, + [a1 cube_intValueWithBase:0], [a2 cube_intValueWithBase:0], + [a3 cube_intValueWithBase:0], [a4 cube_intValueWithBase:0]); + }) Index: src/menus.m ================================================================== --- src/menus.m +++ src/menus.m @@ -2,10 +2,11 @@ #include "cube.h" #import "Menu.h" +#import "Command.h" #import "DynamicEntity.h" #import "MenuItem.h" static OFMutableArray *menuStack; static OFMutableArray *menus; @@ -18,23 +19,20 @@ [player1 resetMovement]; if (vmenu == 1) menus[1].menusel = 0; } -void -showmenu(OFString *name) -{ +COMMAND(showmenu, ARG_1STR, ^(OFString *name) { int i = 0; for (Menu *menu in menus) { if (i > 1 && [menu.name isEqual:name]) { menuset(i); return; } i++; } -} -COMMAND(showmenu, ARG_1STR) +}) void sortmenu() { [menus[0].items sort]; @@ -95,11 +93,14 @@ if (menus == nil) menus = [[OFMutableArray alloc] init]; [menus addObject:[Menu menuWithName:name]]; } -COMMAND(newmenu, ARG_1STR) + +COMMAND(newmenu, ARG_1STR, ^(OFString *name) { + newmenu(name); +}) void menumanual(int m, int n, OFString *text) { if (n == 0) @@ -107,21 +108,18 @@ MenuItem *item = [MenuItem itemWithText:text action:@""]; [menus[m].items addObject:item]; } -void -menuitem(OFString *text, OFString *action) -{ +COMMAND(menuitem, ARG_2STR, ^(OFString *text, OFString *action) { Menu *menu = menus.lastObject; MenuItem *item = [MenuItem itemWithText:text action:(action.length > 0 ? action : text)]; [menu.items addObject:item]; -} -COMMAND(menuitem, ARG_2STR) +}) bool menukey(int code, bool isdown) { if (vmenu <= 0) Index: src/protos.h ================================================================== --- src/protos.h +++ src/protos.h @@ -8,11 +8,10 @@ extern int variable(OFString *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist); extern void setvar(OFString *name, int i); extern int getvar(OFString *name); extern bool identexists(OFString *name); -extern bool addcommand(OFString *name, void (*fun)(), int narg); extern int execute(OFString *p, bool down); extern void exec(OFString *cfgfile); extern bool execfile(OFIRI *cfgfile); extern void resetcomplete(); extern void complete(OFMutableString *s); Index: src/rendercubes.m ================================================================== --- src/rendercubes.m +++ src/rendercubes.m @@ -1,9 +1,11 @@ // rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills // the vertex array for different cube surfaces. #include "cube.h" + +#import "Command.h" static struct vertex *verts = NULL; int curvert; static int curmaxverts = 10000; @@ -59,16 +61,13 @@ int oh, oy, ox, ogltex; // the o* vars are used by the stripification int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b; int firstindex; bool showm = false; -void -showmip() -{ +COMMAND(showmip, ARG_NONE, ^{ showm = !showm; -} -COMMAND(showmip, ARG_NONE) +}) void mipstats(int a, int b, int c) { if (showm) Index: src/renderextras.m ================================================================== --- src/renderextras.m +++ src/renderextras.m @@ -1,9 +1,10 @@ // renderextras.cpp: misc gl render code and the HUD #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Entity.h" void line(int x1, int y1, float z1, int x2, int y2, float z2) @@ -201,13 +202,11 @@ entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, getvar(@"selxs"), getvar(@"selys")]; } } -void -loadsky(OFString *basename) -{ +COMMAND(loadsky, ARG_1STR, (^(OFString *basename) { static OFString *lastsky = @""; basename = [basename stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]; @@ -228,12 +227,11 @@ &xs, &ys, true)) conoutf(@"could not load sky textures"); } lastsky = basename; -} -COMMAND(loadsky, ARG_1STR) +})) float cursordepth = 0.9f; GLint viewport[4]; GLdouble mm[16], pm[16]; OFVector3D worldpos; Index: src/rendergl.m ================================================================== --- src/rendergl.m +++ src/rendergl.m @@ -1,9 +1,10 @@ // rendergl.cpp: core opengl rendering stuff #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Monster.h" #import "OFString+Cube.h" #ifdef DARWIN @@ -183,30 +184,24 @@ mapping[i][j] = 0; } int curtexnum = 0; -void -texturereset() -{ +COMMAND(texturereset, ARG_NONE, ^{ curtexnum = 0; -} -COMMAND(texturereset, ARG_NONE) +}) -void -texture(OFString *aframe, OFString *name) -{ +COMMAND(texture, ARG_2STR, (^(OFString *aframe, OFString *name) { int num = curtexnum++, frame = aframe.cube_intValue; if (num < 0 || num >= 256 || frame < 0 || frame >= MAXFRAMES) return; mapping[num][frame] = 1; mapname[num][frame] = [name stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]; -} -COMMAND(texture, ARG_2STR) +})) int lookuptexture(int tex, int *xs, int *ys) { int frame = 0; // other frames? Index: src/rendermd2.m ================================================================== --- src/rendermd2.m +++ src/rendermd2.m @@ -1,9 +1,10 @@ // rendermd2.cpp: loader code adapted from a nehe tutorial #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "MD2.h" #import "MapModelInfo.h" #import "OFString+Cube.h" @@ -52,35 +53,31 @@ mdllookup[name] = m; return m; } -void -mapmodel( - OFString *rad, OFString *h, OFString *zoff, OFString *snap, OFString *name) -{ - MD2 *m = loadmodel([name stringByReplacingOccurrencesOfString:@"\\" - withString:@"/"]); - m.mmi = [MapModelInfo infoWithRad:rad.cube_intValue - h:h.cube_intValue - zoff:zoff.cube_intValue - snap:snap.cube_intValue - name:m.loadname]; - - if (mapmodels == nil) - mapmodels = [[OFMutableArray alloc] init]; - - [mapmodels addObject:m]; -} -COMMAND(mapmodel, ARG_5STR) - -void -mapmodelreset() -{ +COMMAND(mapmodel, ARG_5STR, + ^(OFString *rad, OFString *h, OFString *zoff, OFString *snap, + OFString *name) { + MD2 *m = + loadmodel([name stringByReplacingOccurrencesOfString:@"\\" + withString:@"/"]); + m.mmi = [MapModelInfo infoWithRad:rad.cube_intValue + h:h.cube_intValue + zoff:zoff.cube_intValue + snap:snap.cube_intValue + name:m.loadname]; + + if (mapmodels == nil) + mapmodels = [[OFMutableArray alloc] init]; + + [mapmodels addObject:m]; + }) + +COMMAND(mapmodelreset, ARG_NONE, ^{ [mapmodels removeAllObjects]; -} -COMMAND(mapmodelreset, ARG_NONE) +}) MapModelInfo * getmminfo(int i) { return i < mapmodels.count ? mapmodels[i].mmi : nil; Index: src/savegamedemo.m ================================================================== --- src/savegamedemo.m +++ src/savegamedemo.m @@ -1,10 +1,11 @@ // loading and saving of savegames & demos, dumps the spawn state of all // mapents, the full state of all dynents (monsters + player) #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Entity.h" #import "Monster.h" #ifdef OF_BIG_ENDIAN @@ -127,13 +128,11 @@ data = [player dataBySerializing]; gzwrite(f, data.items, data.count); } } -void -savegame(OFString *name) -{ +COMMAND(savegame, ARG_1STR, (^(OFString *name) { if (!m_classicsp) { conoutf(@"can only save classic sp games"); return; } @@ -141,12 +140,11 @@ OFIRI *IRI = [Cube.sharedInstance.userDataIRI IRIByAppendingPathComponent:path]; savestate(IRI); stop(); conoutf(@"wrote %@", IRI.string); -} -COMMAND(savegame, ARG_1STR) +})) void loadstate(OFIRI *IRI) { stop(); @@ -182,19 +180,16 @@ conoutf(@"aborting: savegame/demo from a different version of " @"cube or cpu architecture"); stop(); } -void -loadgame(OFString *name) -{ +COMMAND(loadgame, ARG_1STR, (^(OFString *name) { OFString *path = [OFString stringWithFormat:@"savegames/%@.csgz", name]; OFIRI *IRI = [Cube.sharedInstance.userDataIRI IRIByAppendingPathComponent:path]; loadstate(IRI); -} -COMMAND(loadgame, ARG_1STR) +})) void loadgameout() { stop(); @@ -266,13 +261,11 @@ int starttime = 0; int playbacktime = 0; int ddamage, bdamage; OFVector3D dorig; -void -record(OFString *name) -{ +COMMAND(record, ARG_1STR, (^(OFString *name) { if (m_sp) { conoutf(@"cannot record singleplayer games"); return; } @@ -287,12 +280,11 @@ gzputi(cn); conoutf(@"started recording demo to %@", IRI.string); demorecording = true; starttime = lastmillis; ddamage = bdamage = 0; -} -COMMAND(record, ARG_1STR) +})) void demodamage(int damage, const OFVector3D *o) { ddamage = damage; @@ -335,20 +327,17 @@ // FIXME: add all other client state which is not send through // the network } } -void -demo(OFString *name) -{ +COMMAND(demo, ARG_1STR, (^(OFString *name) { OFString *path = [OFString stringWithFormat:@"demos/%@.cdgz", name]; OFIRI *IRI = [Cube.sharedInstance.userDataIRI IRIByAppendingPathComponent:path]; loadstate(IRI); demoloading = true; -} -COMMAND(demo, ARG_1STR) +})) void stopreset() { conoutf(@"demo stopped (%d msec elapsed)", lastmillis - starttime); @@ -547,15 +536,12 @@ } } // if(player1->state!=CS_DEAD) showscores(false); } -void -stopn() -{ +COMMAND(stop, ARG_NONE, ^{ if (demoplayback) stopreset(); else stop(); conoutf(@"demo stopped"); -} -COMMANDN(stop, stopn, ARG_NONE) +}) Index: src/serverbrowser.m ================================================================== --- src/serverbrowser.m +++ src/serverbrowser.m @@ -2,10 +2,11 @@ // management #include "SDL_thread.h" #include "cube.h" +#import "Command.h" #import "ResolverResult.h" #import "ResolverThread.h" #import "ServerInfo.h" static OFMutableArray *resolverthreads; @@ -123,10 +124,14 @@ if (servers == nil) servers = [[OFMutableArray alloc] init]; [servers addObject:[ServerInfo infoWithName:servername]]; } + +COMMAND(addserver, ARG_1STR, ^(OFString *servername) { + addserver(servername); +}) void pingservers() { ENetBuffer buf; @@ -243,11 +248,11 @@ if (!--maxmenu) return; }]; } -void +static void servermenu() { if (pingsock == ENET_SOCKET_NULL) { pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL); resolverinit(1, 1000); @@ -260,13 +265,15 @@ refreshservers(); menuset(1); } -void -updatefrommaster() -{ +COMMAND(servermenu, ARG_NONE, ^{ + servermenu(); +}) + +COMMAND(updatefrommaster, ARG_NONE, ^{ const int MAXUPD = 32000; unsigned char buf[MAXUPD]; unsigned char *reply = retrieveservers(buf, MAXUPD); if (!*reply || strstr((char *)reply, "") || strstr((char *)reply, "")) @@ -274,15 +281,11 @@ else { [servers removeAllObjects]; execute(@((char *)reply), true); } servermenu(); -} - -COMMAND(addserver, ARG_1STR) -COMMAND(servermenu, ARG_NONE) -COMMAND(updatefrommaster, ARG_NONE) +}) void writeservercfg() { FILE *f = fopen("servers.cfg", "w"); Index: src/sound.m ================================================================== --- src/sound.m +++ src/sound.m @@ -1,7 +1,8 @@ #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #include VARP(soundvol, 0, 255, 255); @@ -45,13 +46,11 @@ nosound = true; } Mix_AllocateChannels(MAXCHAN); } -void -music(OFString *name) -{ +COMMAND(music, ARG_1STR, (^(OFString *name) { if (nosound) return; stopsound(); @@ -67,19 +66,16 @@ IRI.fileSystemRepresentation.UTF8String)) != NULL) { Mix_PlayMusic(mod, -1); Mix_VolumeMusic((musicvol * MAXVOL) / 255); } } -} -COMMAND(music, ARG_1STR) +})) static OFMutableData *samples; static OFMutableArray *snames; -int -registersound(OFString *name) -{ +COMMAND(registersound, ARG_1EST, ^int(OFString *name) { int i = 0; for (OFString *iter in snames) { if ([iter isEqual:name]) return i; @@ -96,12 +92,11 @@ withString:@"/"]]; Mix_Chunk *sample = NULL; [samples addItem:&sample]; return samples.count - 1; -} -COMMAND(registersound, ARG_1EST) +}) void cleansound() { if (nosound) @@ -214,11 +209,8 @@ newsoundloc(chan, loc); updatechanvol(chan, loc); } -void -sound(int n) -{ +COMMAND(sound, ARG_1INT, ^(int n) { playsound(n, NULL); -} -COMMAND(sound, ARG_1INT) +}) Index: src/weapon.m ================================================================== --- src/weapon.m +++ src/weapon.m @@ -1,9 +1,10 @@ // weapon.cpp: all shooting and effects code #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Monster.h" #import "OFString+Cube.h" #import "Projectile.h" @@ -60,18 +61,15 @@ reloadtime(int gun) { return guns[gun].attackdelay; } -void -weapon(OFString *a1, OFString *a2, OFString *a3) -{ +COMMAND(weapon, ARG_3STR, ^(OFString *a1, OFString *a2, OFString *a3) { selectgun((a1.length > 0 ? a1.cube_intValue : -1), (a2.length > 0 ? a2.cube_intValue : -1), (a3.length > 0 ? a3.cube_intValue : -1)); -} -COMMAND(weapon, ARG_3STR) +}) // create random spread of rays for the shotgun void createrays(const OFVector3D *from, const OFVector3D *to) { Index: src/world.m ================================================================== --- src/world.m +++ src/world.m @@ -1,9 +1,10 @@ // world.cpp: core map management stuff #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #import "Entity.h" #import "Monster.h" extern OFString *entnames[]; // lookup from map entities above to strings @@ -89,11 +90,14 @@ execute(aliasname, true); if (type == 2) [Monster endSinglePlayerWithAllKilled:false]; } -COMMAND(trigger, ARG_2INT) + +COMMAND(trigger, ARG_2INT, ^(int tag, int type, bool savegame) { + trigger(tag, type, savegame); +}) // main geometric mipmapping routine, recursively rebuild mipmaps within block // b. tries to produce cube out of 4 lower level mips as well as possible, sets // defer to 0 if mipped cube is a perfect mip, i.e. can be rendered at this mip // level indistinguishable from its constituent cubes (saves considerable @@ -302,13 +306,11 @@ }]; return (bdist == 99999 ? -1 : best); } -void -entproperty(int prop, int amount) -{ +COMMAND(entproperty, ARG_2INT, ^(int prop, int amount) { int e = closestent(); if (e < 0) return; switch (prop) { case 0: @@ -322,15 +324,13 @@ break; case 3: ents[e].attr4 += amount; break; } -} +}) -void -delent() -{ +COMMAND(delent, ARG_NONE, ^{ int e = closestent(); if (e < 0) { conoutf(@"no more entities"); return; } @@ -338,11 +338,11 @@ conoutf(@"%@ entity deleted", entnames[t]); ents[e].type = NOTUSED; addmsg(1, 10, SV_EDITENT, e, NOTUSED, 0, 0, 0, 0, 0, 0, 0); if (t == LIGHT) calclight(); -} +}) int findtype(OFString *what) { for (int i = 0; i < MAXENTTYPES; i++) @@ -395,13 +395,11 @@ calclight(); return e; } -void -clearents(OFString *name) -{ +COMMAND(clearents, ARG_1STR, ^(OFString *name) { int type = findtype(name); if (noteditmode() || multiplayer()) return; @@ -409,12 +407,11 @@ if (e.type == type) e.type = NOTUSED; if (type == LIGHT) calclight(); -} -COMMAND(clearents, ARG_1STR) +}) static unsigned char scalecomp(unsigned char c, int intens) { int n = c * intens / 100; @@ -421,13 +418,11 @@ if (n > 255) n = 255; return n; } -void -scalelights(int f, int intens) -{ +COMMAND(scalelights, ARG_2INT, ^(int f, int intens) { for (Entity *e in ents) { if (e.type != LIGHT) continue; e.attr1 = e.attr1 * f / 100; @@ -442,12 +437,11 @@ e.attr4 = scalecomp(e.attr4, intens); } } calclight(); -} -COMMAND(scalelights, ARG_2INT) +}) int findentity(int type, int index) { for (int i = index; i < ents.count; i++) @@ -549,22 +543,16 @@ toggleedit(); execute(@"fullbright 1", true); } } -void -mapenlarge() -{ - empty_world(-1, false); -} - -void -newmap(int i) -{ - empty_world(i, false); -} - -COMMAND(mapenlarge, ARG_NONE) -COMMAND(newmap, ARG_1INT) -COMMANDN(recalc, calclight, ARG_NONE) -COMMAND(delent, ARG_NONE) -COMMAND(entproperty, ARG_2INT) +COMMAND(mapenlarge, ARG_NONE, ^{ + empty_world(-1, false); +}) + +COMMAND(newmap, ARG_1INT, ^(int i) { + empty_world(i, false); +}) + +COMMAND(recalc, ARG_NONE, ^{ + calclight(); +}) Index: src/worldio.m ================================================================== --- src/worldio.m +++ src/worldio.m @@ -1,9 +1,10 @@ // worldio.cpp: loading & saving of maps and savegames #include "cube.h" +#import "Command.h" #import "Entity.h" struct persistent_entity { short x, y, z; // cube aligned position short attr1; @@ -254,11 +255,14 @@ spurge; gzclose(f); conoutf(@"wrote map file %@", cgzname); settagareas(); } -COMMANDN(savemap, save_world, ARG_1STR) + +COMMAND(savemap, ARG_1STR, ^(OFString *mname) { + save_world(mname); +}) void load_world(OFString *mname) // still supports all map formats that have existed // since the earliest cube betas! { Index: src/worldocull.m ================================================================== --- src/worldocull.m +++ src/worldocull.m @@ -1,23 +1,21 @@ // worldocull.cpp: occlusion map and occlusion test #include "cube.h" +#import "Command.h" #import "DynamicEntity.h" #define NUMRAYS 512 float rdist[NUMRAYS]; bool ocull = true; float odist = 256; -void -toggleocull() -{ +COMMAND(toggleocull, ARG_NONE, ^{ ocull = !ocull; -} -COMMAND(toggleocull, ARG_NONE) +}) // constructs occlusion map: cast rays in all directions on the 2d plane and // record distance. done exactly once per frame. void